summaryrefslogtreecommitdiffstats
path: root/private/mvdm/vdmredir/vrremote.c
blob: 9b2de21f85f6eb6bb2d0d234b234b43ca75f8775 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
/*++

Copyright (c) 1987-1991  Microsoft Corporation

Module Name:

    vrremote.c

Abstract:

    This module contains a routine VrRemoteApi which is a 16-bit only version
    of RxRemoteApi from the net\rpcxlate project. This routine supports remoted
    lanman APIs from a Virtual Dos Machine.

    This routine does not have to convert 32-16-32, but rather receives 16-bit
    data and sends a 16-bit transaction packet either to a down-level server
    or an NT-level server which must be running XactSrv to respond to this
    request.

    This routine and the support routines in vrremutl.c were lifted from the
    lanman project

    Note: since this is 32-bit code which deals with 16-bit data in a few places,
    32-bit data items should be used where possible and only use 16-bit items
    where unavoidable

    Contents of this file:

        VrRemoteApi
        VrTransaction
        (VrpGetStructureSize)
        (VrpGetArrayLength)
        (VrpGetFieldSize)
        (VrpConvertReceiveBuffer)
        (VrpConvertVdmPointer)
        (VrpPackSendBuffer)

Author:

    Richard L Firth (rfirth) 24-Oct-1991

Environment:

    Flat 32-bit, user space

Revision History:

    21-Oct-1991 rfirth
        Created

--*/

#include <nt.h>
#include <ntrtl.h>      // ASSERT, DbgPrint
#include <nturtl.h>
#include <windows.h>
#include <softpc.h>     // x86 virtual machine definitions
#include <vrdlctab.h>
#include <vdmredir.h>   // common Vr stuff
#include <lmcons.h>
#include <lmerr.h>
#include <lmwksta.h>    // NetWkstaGetInfo
#include <lmapibuf.h>   // NetApiBufferFree
#include <apiworke.h>   // REM_MAX_PARMS
#include <mvdm.h>       // FETCHWORD
#include <vrremote.h>   // prototypes
#include <remtypes.h>
#include <smbgtpt.h>
#include <rxp.h>        // RxpTransactSmb
#include <apinums.h>    // API_W numbers
#include <string.h>
#include <vrdebug.h>

//
// Global data.
//

unsigned short remapi_err_flag;

//
// code
//


NET_API_STATUS
VrTransaction(
    IN      LPSTR   ServerName,
    IN      LPBYTE  SendParmBuffer,
    IN      DWORD   SendParmBufLen,
    IN      LPBYTE  SendDataBuffer,
    IN      DWORD   SendDataBufLen,
    OUT     LPBYTE  ReceiveParmBuffer,
    IN      DWORD   ReceiveParmBufLen,
    IN      LPBYTE  ReceiveDataBuffer,
    IN OUT  LPDWORD ReceiveDataBufLen,
    IN      BOOL    NullSessionFlag
    )

/*++

Routine Description:

    Sends a transaction request to a server and receives a response

Arguments:

    ServerName          - to send request to
    SendParmBuffer      - send parameters
    SendParmBufLen      - length of send parameters
    SendDataBuffer      - send data
    SendDataBufLen      - length of send data
    ReceiveParmBuffer   - receive parameter buffer
    ReceiveParmBufLen   - length of receive parameter buffer
    ReceiveDataBuffer   - where to receive data
    ReceiveDataBufLen   - length of data buffer
    NullSessionFlag     - set if we are to use a null session

Return Value:

    NET_API_STATUS
        Success - NERR_Success
        Failure -

--*/

{
    NET_API_STATUS  status;

    status = RxpTransactSmb(ServerName,

                            //
                            // BUGBUG - transport name?
                            //

                            NULL,
                            SendParmBuffer,
                            SendParmBufLen,
                            SendDataBuffer,
                            SendDataBufLen,
                            ReceiveParmBuffer,
                            ReceiveParmBufLen,
                            ReceiveDataBuffer,
                            ReceiveDataBufLen,
                            NullSessionFlag
                            );
    if (status == NERR_Success) {
    }

    return status;
}


NET_API_STATUS
VrRemoteApi(
    IN  DWORD   ApiNumber,
    IN  LPBYTE  ServerNamePointer,
    IN  LPSTR   ParameterDescriptor,
    IN  LPSTR   DataDescriptor,
    IN  LPSTR   AuxDescriptor OPTIONAL,
    IN  BOOL    NullSessionFlag
    )

/*++

Routine Description:

    This routine creates and sends a 16-bit transaction SMB containing the
    parameters and data required for a remoted function call. Any received
    data is copied back into the caller's data space as 16-bit data. This
    function is being called on behalf of a VDM process which in turn is
    running as a virtual Intel 286 which means:

        * little endian
        * pointers are 32-bits <segment|selector>:<offset>
        * stack is 16-bits wide and EXPANDS DOWN

    This routine is called as a result of the NetIRemoteAPI function being
    called in the VDM. This is an internal function and so the descriptor
    parameters are trusted. However, if the original (16-bit) caller gave
    us a bad buffer address or length then the results will be unpredictable.

    The original API which called NetIRemoteAPI was a pascal calling convention
    routine so if its parameter list was:

    FAR PASCAL
    NetRoutine(server_name, buffer_pointer, buffer_length, &bytes_read, &total);

    the stack would look like this: (note: all pointers are far)

                             +----------------+
            stack pointer => | ip             | routine was called far
                             +----------------+
                             | cs             |
                             +----------------+
                             | &total         | Offset
                             +----------------+
                             | &total         | Segment
                             +----------------+
                             | &bytes_read    | Offset
                             +----------------+
                             | &bytes_read    | Segment
                             +----------------+
                             | buffer_length  |
                             +----------------+
                             | buffer_pointer | Offset
                             +----------------+
                             | buffer_pointer | Segment
                             +----------------+
                             | server_name    | Offset
                             +----------------+
                             | server_name    | Segment
                             +----------------+

    Assumes:

        BYTE  is an 8-bit quantity
        WORD  is a 16-bit quantity
        DWORD is a 32-bit quantity
        LPSTR is a 32-bit flat pointer to an 8-bit quantity

Arguments:

    ApiNumber           - Function number of the API required

    ServerNamePointer   - Flat 32-bit pointer to address of 32-bit segmented
                          far pointer to ASCIZ server name in Dos image.
                          Immediately prior to this is a pascal calling
                          convention stack of 16-bit caller parameters (see
                          above). The server name identifies the server at
                          which the API is to be executed

    ParameterDescriptor - Flat 32-bit pointer to ASCIZ string which describes
                          caller parameters

    DataDescriptor      - Flat 32-bit pointer to ASCIZ string which describes
                          data structure in caller buffer (if any) or structure
                          of data returned from server

    AuxDescriptor       - Flat 32-bit pointer to ASCIZ string which describes
                          auxiliary data structures in send buffer (if any) or
                          structure of aux data returned from server

    NullSessionFlag     - TRUE if we are to use a NULL session

Return Value:

    NET_API_STATUS
        Success - 0
        Failure - NERR_InternalError
                    Return this when we have a bad descriptor character or we
                    blow an internal limit. Basically if we return this its
                    safe to assume the DOS box handed us some garbage (typically
                    a descriptor string got trashed etc)

--*/

{

//
// redefine our parameter identifiers as old-code identifiers
//

#define api_num         ApiNumber
#define servername_ptr  ServerNamePointer
#define parm_str        ParameterDescriptor
#define data_str        DataDescriptor
#define aux_str         AuxDescriptor

//
// define a macro to perform the buffer checking and length and pointer
// manipulation. Either quits the routine and returns ERROR_INVALID_PARAMETER
// or updates parm_len and parm_pos to indicate the next available positions
// and makes this_parm_pos available as the current position to write into
//

#define CHECK_PARAMETERS(len)           \
{                                       \
    parm_len += len;                    \
    if (parm_len > sizeof(parm_buf)) {  \
        return ERROR_INVALID_PARAMETER; \
    }                                   \
    this_parm_pos = parm_pos;           \
    parm_pos += len;                    \
}

    //
    // 32-bit flat pointers and buffers
    //

    BYTE    parm_buf[REM_MAX_PARMS];    // Parameter buffer
    BYTE    computerName[CNLEN+1];
    LPBYTE  parm_pos;                   // Pointer into parm_buf
    LPBYTE  this_parm_pos;              // next place to write in parm_buf
    LPBYTE  parm_ptr;                   // Ponter to stack parms
    LPSTR   l_parm;                     // Used to index parm_str
    LPSTR   l_data;                     // Used to index data_str
    LPSTR   l_aux;                      // Used to index aux_str
    LPBYTE  rcv_data_ptr;               // Pointer to callers rcv buf
    LPBYTE  send_data_ptr;              // Ptr to send buffer to use
    LPBYTE  wkstaInfo;
    LPBYTE  serverName;

    //
    // lengths - 32-bit variables (even though actual lengths are quite small)
    //

    DWORD   parm_len;                   // Length of send parameters
    DWORD   ret_parm_len;               // Length of expected parms
    DWORD   rcv_data_length;            // Length of callers rcv buf
    DWORD   send_data_length;           // Length of callers send buf
    DWORD   parm_num;                   // Callers value for parm_num
    DWORD   struct_size;                // Size of fixed data struct
    DWORD   aux_size;                   // Size of aux data struct
    DWORD   num_struct;                 // Loop count for ptr fixup

    //
    // 16-bit quantities - only used when converting received 16-bit data in
    // caller's receive buffer
    //

    WORD    ReceiveBufferSelector;
    WORD    ReceiveBufferOffset;
    WORD    converter;                  // For pointer fixups

    //
    // various flags
    //

    BOOL    rcv_dl_flag;                // Expect return data flag
    BOOL    send_dl_flag;               // Send data buffer flag
    BOOL    rcv_dp_flag;                // rcv buf ptr present flag
    BOOL    send_dp_flag;               // send buf ptr present flag
    BOOL    parm_num_flag;              // API has a parm_num
    BOOL    alloc_flag;

    //
    // misc. variables
    //

    DWORD   aux_pos;                    // aux structure expected
    DWORD   no_aux_check;               // check flag
    int     len;                        // General purpose length
    API_RET_TYPE    status;             // Return status from remote

    UNICODE_STRING uString;
    ANSI_STRING aString;
    LPWSTR uncName;
    NTSTATUS ntstatus;


    //
    // Clear the internal error flag
    //

    remapi_err_flag = 0;

    //
    // Set found parameter flags to FALSE and ponters to NULL
    //

    rcv_dl_flag     = FALSE;
    send_dl_flag    = FALSE;
    rcv_dp_flag     = FALSE;
    alloc_flag      = FALSE;
    send_dp_flag    = FALSE;
    parm_num_flag   = FALSE;
    rcv_data_length = 0;
    send_data_length= 0;
    parm_num        = 0;
    rcv_data_ptr    = NULL;
    send_data_ptr   = NULL;

    //
    // Set up parm_ptr to point to first of the callers parmeters
    //

    parm_ptr = servername_ptr;
    parm_pos = parm_buf;
    ret_parm_len = 2 * sizeof(WORD);    /* Allow for return status & offset */


    //
    // parse parameter descriptor/build parameter buffer for transaction
    // and get interesting information from 16-bit parameters
    // When finished, the parameter buffer looks like this:
    //
    //  <api_num><parm_desc><data_desc><parms>[<aux_desc>]
    //
    // Remember: DOS only deals with ASCII characters
    //

    *((LPWORD)parm_pos)++ = (WORD)ApiNumber;
    parm_len = sizeof(WORD);

    len = strlen(ParameterDescriptor) + 1;
    parm_len += len;
    if (parm_len > sizeof(parm_buf)) {
        return NERR_InternalError;
    }
    l_parm = parm_pos;
    RtlCopyMemory(parm_pos, ParameterDescriptor, len);
    parm_pos += len;

    len = strlen(DataDescriptor) + 1;
    parm_len += len;
    if (parm_len > sizeof(parm_buf)) {
        return NERR_InternalError;
    }
    l_data = parm_pos;
    RtlCopyMemory(parm_pos, DataDescriptor, len);
    parm_pos += len;

    //
    // parse the parameter descriptor strings. Remember interesting things such
    // as pointers to buffers, buffer lengths, etc.
    //

    for (; *l_parm != '\0'; l_parm++) {
        switch(*l_parm) {
        case REM_WORD:
            CHECK_PARAMETERS(sizeof(WORD));
            parm_ptr -= sizeof(WORD);
            SmbMoveUshort((LPWORD)this_parm_pos, (LPWORD)parm_ptr);
            break;

        case REM_ASCIZ: {
                LPSTR   pstring;

                //
                // the parameter is a pointer to a string. Read the string
                // pointer from the caller's stack then check the string proper.
                // If the pointer is NULL, change the parameter descriptor sent
                // in the SMB to indicate the pointer was NULL at this end
                //

                parm_ptr -= sizeof(LPSTR);
                pstring = LPSTR_FROM_POINTER(parm_ptr);
                if (pstring == NULL) {
                    *(l_parm) = REM_NULL_PTR;
                    break;
                }
                len = strlen(pstring) + 1;
                CHECK_PARAMETERS(len);
                RtlCopyMemory(this_parm_pos, pstring, len);
            }
            break;

        case REM_BYTE_PTR:
        case REM_WORD_PTR:
        case REM_DWORD_PTR: {
                LPBYTE  pointer;

                parm_ptr -= sizeof(LPBYTE);
                pointer = LPBYTE_FROM_POINTER(parm_ptr);
                if (pointer == NULL) {
                    *(l_parm) = REM_NULL_PTR; /* Indicate null pointer */
                    break;
                }
                len = VrpGetArrayLength(l_parm, &l_parm);
                CHECK_PARAMETERS(len);
                RtlCopyMemory(this_parm_pos, pointer, len);
            }
            break;


        case REM_RCV_WORD_PTR:
        case REM_RCV_BYTE_PTR:
        case REM_RCV_DWORD_PTR: {
                LPBYTE  pointer;

                parm_ptr -= sizeof(LPBYTE*);
                pointer = LPBYTE_FROM_POINTER(parm_ptr);

                //
                // Added this test for a NULL pointer to allow for
                // a reserved field (currently MBN) to be a recv
                // pointer. - ERICPE 7/19/89
                //

                if (pointer == NULL) {
                    *(l_parm) = REM_NULL_PTR;
                    break;
                }
                ret_parm_len += VrpGetArrayLength(l_parm, &l_parm);
                if (ret_parm_len > sizeof(parm_buf)) {
                    ASSERT(FALSE);
                    return NERR_InternalError;
                }
            }
            break;

        case REM_DWORD:
            CHECK_PARAMETERS(sizeof(DWORD));
            parm_ptr -= sizeof(DWORD);
            SmbMoveUlong((LPDWORD)this_parm_pos, (LPDWORD)parm_ptr);
            break;

        case REM_RCV_BUF_LEN:
            CHECK_PARAMETERS(sizeof(WORD));
            parm_ptr -= sizeof(WORD);
            SmbMoveUshort((LPWORD)this_parm_pos, (LPWORD)parm_ptr);
            rcv_data_length = (DWORD)SmbGetUshort((LPWORD)parm_ptr);
            rcv_dl_flag = TRUE;
#ifdef VR_DIAGNOSE
            DbgPrint("VrRemoteApi: rcv_data_length=%x\n", rcv_data_length);
#endif
            break;

        case REM_RCV_BUF_PTR:
            parm_ptr -= sizeof(LPBYTE);
            ReceiveBufferOffset = GET_OFFSET(parm_ptr);
            ReceiveBufferSelector = GET_SELECTOR(parm_ptr);
            rcv_data_ptr = LPBYTE_FROM_POINTER(parm_ptr);
            rcv_dp_flag = TRUE;
#ifdef VR_DIAGNOSE
            DbgPrint("VrRemoteApi: Off=%x, Sel=%x, data_ptr=%x\n",
                ReceiveBufferOffset, ReceiveBufferSelector, rcv_data_ptr);
#endif
            break;

        case REM_SEND_BUF_PTR:
            parm_ptr -= sizeof(LPBYTE);
            send_data_ptr = LPBYTE_FROM_POINTER(parm_ptr);
            send_dp_flag = TRUE;
            break;

        case REM_SEND_BUF_LEN:
            parm_ptr -= sizeof(WORD);
            send_data_length = (DWORD)SmbGetUshort((LPWORD)parm_ptr);
            send_dl_flag = TRUE;
            break;

        case REM_ENTRIES_READ:
            ret_parm_len += sizeof(WORD);
            if (ret_parm_len > sizeof(parm_buf)) {
                ASSERT(FALSE);
                return NERR_InternalError;
            }
            parm_ptr -= sizeof(LPBYTE);
            break;

        case REM_PARMNUM:
            CHECK_PARAMETERS(sizeof(WORD));
            parm_ptr -= sizeof(WORD);
            parm_num = (DWORD)SmbGetUshort((LPWORD)parm_ptr);
            SmbMoveUshort((LPWORD)this_parm_pos, (LPWORD)parm_ptr);
            parm_num_flag = TRUE;
            break;

        case REM_FILL_BYTES:

            //
            // This is a rare type but is needed to ensure that the
            // send paramteres are at least as large as the return
            // parameters so that buffer management can be simplified
            // on the server.
            //

            len = VrpGetArrayLength(l_parm, &l_parm);
            CHECK_PARAMETERS(len);
            break;

        default:        /* Could be a digit from NULL send array */
            break;
        }
    }

    //
    // The parameter buffer now contains ;
    // api_num      - word
    // parm_str     - asciz, (NULL c,i,f,z identifiers replaced with Z.
    // data_str     - asciz
    // parameters   - as identified by parm_str.
    //

    //
    // For the receive buffer there is no data to set up for the call
    // but there might have been an REM_AUX_COUNT descriptor in data_str
    // which requires the aux_str to be copied onto the end of the
    // parameter buffer.
    //

    if (rcv_dp_flag || send_dp_flag) {
        //
        // Find the length of the fixed length portion of the data
        // buffer.
        //

        struct_size = VrpGetStructureSize(l_data, &aux_pos);
        if (aux_pos != -1) {
            l_aux = aux_str;
            len = strlen(l_aux) + 1;       /* Length of aux descriptor */
            CHECK_PARAMETERS(len);
            RtlCopyMemory(this_parm_pos, aux_str, len);
            aux_size = VrpGetStructureSize(l_aux, &no_aux_check);
            if (no_aux_check != -1) {        /* Error if N in aux_str */
                ASSERT(FALSE);
                return NERR_InternalError;
            }
        }
    }

    //
    // For a send buffer the data pointed to in the fixed structure
    // must be copied into the send buffer. Any pointers which already
    // point in the send buffer are NULLed as it is illegal to use
    // the buffer for the send data, it is our transport buffer.
    // NOTE - if parmnum was specified the buffer contains only that
    // element of the structure so no length checking is needed at this
    // side. A parmnum for a pointer type means that the data is at the
    // start of the buffer so there is no copying to be done.
    //


    if (send_dp_flag) {
        //
        // Only process buffer if no parm_num and this is not a block send
        // (no data structure) or an asciz concatenation send
        //

        if ((parm_num == 0) && (*l_data != REM_DATA_BLOCK)) {
            status = VrpPackSendBuffer(
                        &send_data_ptr,
                        &send_data_length,
                        &alloc_flag,
                        data_str,
                        aux_str,
                        struct_size,
                        aux_pos,
                        aux_size,
                        parm_num_flag,
                        FALSE
                        );
            if (status != 0) {
                return status;
            }
        }
    }

    //
    // Check for an internal error prior to issuing the transaction
    //

    if (remapi_err_flag != 0) {
        if (alloc_flag) {
            LocalFree(send_data_ptr);
        }
        return NERR_InternalError;
    }

    //
    // get the server name. If it is NULL then we are faking a local API call
    // by making a remote call to XactSrv on this machine. Fill in our computer
    // name
    //

    serverName = LPSTR_FROM_POINTER(servername_ptr);

////////////////////////////////////////////////////////////////////////////////
//// is this actually required any longer?

    if (serverName == NULL) {
        status = NetWkstaGetInfo(NULL, 100, &wkstaInfo);
        if (status) {
            if (alloc_flag) {
                LocalFree(send_data_ptr);
            }
            return status;
        } else {
            computerName[0] = computerName[1] = '\\';

            //
            // BUGBUG - Unicode - ASCII conversion here
            //

            strcpy(computerName+2,
                    ((LPWKSTA_INFO_100)wkstaInfo)->wki100_computername);
            NetApiBufferFree(wkstaInfo);
            serverName = computerName;
#ifdef VR_DIAGNOSE
            DbgPrint("VrRemoteApi: computername is %s\n", serverName);
#endif
        }
    }

////////////////////////////////////////////////////////////////////////////////

    //
    // The parameter buffers and data buffers are now set up for
    // sending to the API worker so call transact to send them.
    //

    RtlInitAnsiString(&aString, serverName);
    ntstatus = RtlAnsiStringToUnicodeString(&uString, &aString, (BOOLEAN)TRUE);
    if (!NT_SUCCESS(ntstatus)) {

#if DBG
        IF_DEBUG(NETAPI) {
            DbgPrint("VrRemoteApi: Unexpected situation: RtlAnsiStringToUnicodeString returns %x\n", ntstatus);
        }
#endif

        return ERROR_NOT_ENOUGH_MEMORY;
    }
    uncName = uString.Buffer;

#if DBG
    IF_DEBUG(NETAPI) {
        DbgPrint("VrpTransactVdm: UncName=%ws\n", uncName);
    }
#endif

    status = RxpTransactSmb((LPTSTR)uncName,

                            //
                            // BUGBUG - transport name?
                            //

                            NULL,
                            parm_buf,               // Send parm buffer
                            parm_len,               // Send parm length
                            send_data_ptr,          // Send data buffer
                            send_data_length,       // Send data length
                            parm_buf,               // Rcv prm buffer
                            ret_parm_len,           // Rcv parm length
                            rcv_data_ptr,           // Rcv data buffer
                            &rcv_data_length,       // Rcv data length
                            NullSessionFlag
                            );
    RtlFreeUnicodeString(&uString);

    if (status) {
#ifdef VR_DIAGNOSE
        DbgPrint("Error: VrRemoteApi: RxpTransactSmb returns %d(%x)\n",
            status, status);
#endif
        switch (status) {
        case NERR_BufTooSmall:  /* No data returned from API worker */
            rcv_data_length = 0;
            break;

        case ERROR_MORE_DATA:   /* Just a warning for the caller */
            break;

        case NERR_TooMuchData:  /* Just a warning for the caller */
            break;

        default:
            rcv_data_length = 0;
            break;
        }
    }

    /* The API call was successful. Now translate the return buffers
     * into the local API format.
     *
     * First copy any data from the return parameter buffer into the
     * fields pointed to by the original call parmeters.
     * The return parameter buffer contains;
     *      status,         (unsigned short)
     *      converter,      (unsigned short)
     *      ...             - fields described by rcv ptr types in parm_str
     */

    parm_pos = parm_buf + sizeof(WORD);
    converter = (WORD)SmbGetUshort((LPWORD)parm_pos);
    parm_pos += sizeof(WORD);

    //
    // Set up parm_ptr to point to first of the callers parmeters
    //

    parm_ptr = servername_ptr;

    //
    // set default value of num_struct to 1, if data, 0 if no data
    //

    num_struct = (DWORD)((*data_str == '\0') ? 0 : 1);

    for (; *parm_str != '\0'; parm_str++) {
        switch (*parm_str) {
        case REM_RCV_WORD_PTR:
        case REM_RCV_BYTE_PTR:
        case REM_RCV_DWORD_PTR: {
                LPBYTE  ptr;

                parm_ptr -= sizeof(LPBYTE*);
                ptr = LPBYTE_FROM_POINTER(parm_ptr);

                //
                // if the rcv buffer given to us by the user is NULL,
                // (one currently can be - it is an MBZ parameter for
                // now in the log read apis...), don't attempt to
                // copy anything. len will be garbage in this
                // case, so don't update parm_pos either.  All we
                // use VrpGetArrayLength for is to update parm_str if
                // the parameter was NULL.
                //

                if (ptr != NULL) {
                    len = VrpGetArrayLength(parm_str, &parm_str);
                    RtlCopyMemory(ptr, parm_pos, len);

                    //
                    // This gross hack is to fix the problem that a
                    // down level spooler (Lan Server 1.2)
                    // do not perform level checking
                    // on the w functions of the api(s):
                    // DosPrintQGetInfo
                    // and thus can return NERR_Success
                    // and bytesavail == 0.  This combination
                    // is technically illegal, and results in
                    // us attempting to unpack a buffer full of
                    // garbage.  The following code detects this
                    // condition and resets the amount of returned
                    // data to zero so we do not attempt to unpack
                    // the buffer.  Since we know the reason for the
                    // mistake at the server end is that we passed
                    // them a new level, we return ERROR_INVALID_LEVEL
                    // in this case.
                    // ERICPE, 5/16/90.
                    //

                    if ((api_num == API_WPrintQGetInfo)
                    && (status == NERR_Success)
                    && (*parm_str == REM_RCV_WORD_PTR)
                    && (*(LPWORD)ptr == 0)) {
                        rcv_data_length = 0;
                        status = ERROR_INVALID_LEVEL;
                    }

                    //
                    // END OF GROSS HACK
                    //

                    parm_pos += len;
                }
            }
            break;

        case REM_ENTRIES_READ: {
                LPWORD  wptr;

                parm_ptr -= sizeof(LPWORD*);
                wptr = (LPWORD)POINTER_FROM_POINTER(parm_ptr);
                num_struct = (DWORD)SmbGetUshort((LPWORD)parm_pos);
                SmbPutUshort((LPWORD)wptr, (WORD)num_struct);
                parm_pos += sizeof(WORD);
            }
            break;

        case REM_FILL_BYTES:
            //
            // Special case, this was not really an input parameter
            // so parm_ptr does not get changed. However, the parm_str
            // pointer must be advanced past the descriptor field so
            // use get VrpGetArrayLength to do this but ignore the
            // return length.
            //

            VrpGetArrayLength(parm_str, &parm_str);
            break;

        default:
            //
            // If the descriptor was not a rcv pointer type then step
            // over the parmeter pointer.
            //

            parm_ptr -= VrpGetFieldSize(parm_str, &parm_str);
        }
    }

    //
    // Now convert all pointer fields in the receive buffer to local
    // pointers.
    //

    if (rcv_dp_flag && (rcv_data_length != 0)) {
        VrpConvertReceiveBuffer(
            rcv_data_ptr,           // lp
            ReceiveBufferSelector,  // word
            ReceiveBufferOffset,    // word
            converter,              // word
            num_struct,             // dword
            data_str,               // lp
            aux_str                 // lp
            );
    }

    if (alloc_flag) {
        LocalFree(send_data_ptr);
    }

    if (remapi_err_flag != 0) {
        return NERR_InternalError;
    }

    return status;
}


DWORD
VrpGetStructureSize(
    IN  LPSTR   Descriptor,
    IN  LPDWORD AuxOffset
    )

/*++

Routine Description:

    Calculates the length of the fixed portion of a structure, based on the
    descriptor for that structure

Arguments:

    Descriptor  - pointer to ASCIZ data descriptor string
    AuxOffset   - pointer to returned dword which is relative position in the
                  data descriptor where a REM_AUX_NUM descriptor was found
                  This will be set to -1 if no aux descriptor found

Return Value:

    Length in bytes of structure described by Descriptor

--*/

{
    DWORD   length;
    char    c;

    *AuxOffset = (DWORD)(-1);
    for (length = 0; (c = *Descriptor) != '\0'; Descriptor++) {
        if (c == REM_AUX_NUM) {
            *AuxOffset = length;
            length += sizeof(WORD);
        } else {
            length += VrpGetFieldSize(Descriptor, &Descriptor);
        }
    }
    return length;
}


DWORD
VrpGetArrayLength(
    IN  LPSTR   Descriptor,
    IN  LPSTR*  pDescriptor
    )

/*++

Routine Description:

    Calculates the length of an array described by an element of a
    descriptor string and update the descriptor string pointer to point
    to the last char in the element of the descriptor string.

Arguments:

    Descriptor  - pointer to ASCIZ descriptor string
    pDescriptor - pointer to address of Descriptor

Return Value:

    Length in bytes of array described by Descriptor

--*/

{
    DWORD   num_elements;
    DWORD   element_length;

    //
    // First set length of an element in the array
    //

    switch (*Descriptor) {
    case REM_WORD:
    case REM_WORD_PTR:
    case REM_RCV_WORD_PTR:
        element_length = sizeof(WORD);
        break;

    case REM_DWORD:
    case REM_DWORD_PTR:
    case REM_RCV_DWORD_PTR:
        element_length = sizeof(DWORD);
        break;

    case REM_BYTE:
    case REM_BYTE_PTR:
    case REM_RCV_BYTE_PTR:
    case REM_FILL_BYTES:
        element_length = sizeof(BYTE);
        break;

    //
    // Warning: following fixes a bug in which "b21" type
    //          combinations in parmeter string will be
    //          handled correctly when pointer to such "bit map"
    //          in the struct is NULL. These two dumbos could
    //          interfere so we  force a success return.
    //

    case REM_ASCIZ:
    case REM_SEND_LENBUF:
    case REM_NULL_PTR:
        return 0;

    default:
        remapi_err_flag = NERR_InternalError;
        ASSERT(FALSE);
        return 0;
    }

    //
    // Now get numeber of elements in the array
    //

    for (num_elements = 0, Descriptor++;
        (*Descriptor <= '9') && (*Descriptor >= '0');
        Descriptor++, (*pDescriptor)++) {
        num_elements = (WORD)((10 * num_elements) + ((WORD)*Descriptor - (WORD)'0'));
    }

    return (num_elements == 0) ? element_length : element_length * num_elements;
}


DWORD
VrpGetFieldSize(
    IN  LPSTR   Descriptor,
    IN  LPSTR*  pDescriptor
    )

/*++

Routine Description:

    Calculates the length of an field described by an element of a
    descriptor string and update the descriptor string pointer to point
    to the last char in the element of the descriptor string.

Arguments:

    Descriptor  - pointer to the descriptor string
    pDescriptor - pointer to the address of the descriptor. On exit
                  this points to the last character in the descriptor
                  just parsed

Return Value:

    Length in bytes of the field parsed

--*/

{
    char c;

    c = *Descriptor;
    if (IS_POINTER(c) || (c == REM_NULL_PTR)) { /* All pointers same size */
        while (*(++Descriptor) <= '9' && *Descriptor >= '0') {
            (*pDescriptor)++;     /* Move ptr to end of field size */
        }
        return sizeof(LPSTR);
    }

    //
    // Here if descriptor was not a pointer type so have to find the field
    // length specifically
    //

    switch (c) {
    case REM_WORD:
    case REM_BYTE:
    case REM_DWORD:
        return VrpGetArrayLength(Descriptor, pDescriptor);

    case REM_AUX_NUM:
    case REM_PARMNUM:
    case REM_RCV_BUF_LEN:
    case REM_SEND_BUF_LEN:
        return sizeof(WORD);

    case REM_DATA_BLOCK:
    case REM_IGNORE:
        return 0;                  /* No structure for this */

    case REM_DATE_TIME:
        return sizeof(DWORD);

    default:
        remapi_err_flag = NERR_InternalError;
#ifdef VR_DIAGNOSE
        DbgPrint("VrpGetFieldSize: offending descriptor is '%c'\n", c);
#endif
        ASSERT(FALSE);
        return 0;
    }
}


VOID
VrpConvertReceiveBuffer(
    IN  LPBYTE  ReceiveBuffer,
    IN  WORD    BufferSelector,
    IN  WORD    BufferOffset,
    IN  WORD    ConverterWord,
    IN  DWORD   NumberStructs,
    IN  LPSTR   DataDescriptor,
    IN  LPSTR   AuxDescriptor
    )

/*++

Routine Description:

    All pointers in the receive buffer are returned from the API worker as
    pointers into the buffer position given to the API on the API worker's
    station. In order to convert them into local pointers the segment
    of each pointer must be set to the segment of the rcv buffer and the offset
    must be set to;

        offset of rcv buffer + offset of pointer - converter word.

    This routine steps through the receive buffer and calls VrpConvertVdmPointer
    to perform the above pointer conversions.

Arguments:

    ReceiveBuffer   - 32-bit flat pointer to 16-bit DOS buffer
    BufferSelector  - 16-bit selector of Dos receive buffer
    BufferOffset    - 16-bit offset of Dos receive buffer
    ConverterWord   - From API worker
    NumberStructs   - Entries read parm (or 1 for GetInfo)
    DataDescriptor  - String for data format
    AuxDescriptor   - string for aux format

Return Value:

    None.

--*/

{
    LPSTR   l_data;
    LPSTR   l_aux;
    DWORD   num_aux;
    DWORD   i, j;
    char    c;


    for (i = 0; i < NumberStructs; i++) {
        //
        // convert all pointers in next primary; if we hit a aux word count
        // remember number of secondary structures
        //

        for (l_data = DataDescriptor, num_aux = 0; c = *l_data; l_data++) {
            if (c == REM_AUX_NUM) {
                num_aux = (DWORD)*(ULPWORD)ReceiveBuffer;
            }
            if (IS_POINTER(c)) {
                VrpConvertVdmPointer(
                    (ULPWORD)ReceiveBuffer,
                    BufferSelector,
                    BufferOffset,
                    ConverterWord
                    );
            }
            ReceiveBuffer += VrpGetFieldSize(l_data, &l_data);
        }

        //
        // convert any pointers in any returned secondary (aux) structures
        //

        for (j = 0; j < num_aux; j++) {
            for (l_aux = AuxDescriptor; c = *l_aux; l_aux++) {
                if (IS_POINTER(c)) {
                    VrpConvertVdmPointer(
                        (ULPWORD)ReceiveBuffer,
                        BufferSelector,
                        BufferOffset,
                        ConverterWord
                        );
                }
                ReceiveBuffer += VrpGetFieldSize(l_aux, &l_aux);
            }
        }
    }
}


VOID
VrpConvertVdmPointer(
    IN  ULPWORD TargetPointer,
    IN  WORD    BufferSegment,
    IN  WORD    BufferOffset,
    IN  WORD    ConverterWord
    )

/*++

Routine Description:

    All pointers in the receive buffer are returned from the API worker as
    pointers into the buffer position given to to the API on the API worker's
    station. In order to convert them into local pointers the segment
    of each pointer must be set to the segment of the rcv buffer and the offset
    must be set to;

        offset of rcv buffer + offset of pointer - converter word.

    The pointer is not converted if it is NULL

Arguments:

    TargetPointer   - 32-bit flat pointer to segmented Dos pointer to convert
    BufferSegment   - 16-bit selector/segment of target buffer in DOS image
    BufferOffset    - 16-bit offset within BufferSegment where buffer starts
    ConverterWord   - 16-bit offset converter word from API worker on server

Return Value:

    None.

--*/

{
    WORD    offset;

    if (*((UCHAR * UNALIGNED *)TargetPointer) != NULL) {
        SET_SELECTOR(TargetPointer, BufferSegment);
        offset = GET_OFFSET(TargetPointer) - ConverterWord;
        SET_OFFSET(TargetPointer, BufferOffset + offset);
    }
}


NET_API_STATUS
VrpPackSendBuffer(
    IN OUT  LPBYTE* SendBufferPtr,
    IN OUT  LPDWORD SendBufLenPtr,
    OUT     LPBOOL  SendBufferAllocated,
    IN OUT  LPSTR   DataDescriptor,
    IN      LPSTR   AuxDescriptor,
    IN      DWORD   StructureSize,
    IN      DWORD   AuxOffset,
    IN      DWORD   AuxSize,
    IN      BOOL    SetInfoFlag,
    IN      BOOL    OkToModifyDescriptor
    )

/*++

Routine Description:

    For a send buffer the data pointed to in the fixed structure
    must be copied into the send buffer. Any pointers which already
    point in the send buffer are NULLed ( or errored if the call is not
    a SetInfo type) as it is illegal to use the buffer for the send data,
    it is our transport buffer

    Note that if the caller's (VDM) buffer is large enough, the variable data
    will be copied there. Eg. if the caller is doing a NetUseAdd which has a
    26 byte fixed structure (use_info_1) and they placed that structure in a
    1K buffer, the remote name will be copied into their own buffer at offset 26.

    The data pointed to is in 16-bit little-endian format; any pointers are
    segmented 16:16 pointers combined in the (thankfully) imitable intel way
    to result in a 20-bit linear (virtual) address

    If this function fails, the caller's buffer pointer and length will not
    have altered. If it succeeds however, *SendBufferPtr and *SendBufLenPtr
    may be different to the values passed, depending on whether
    *SendBufferAllocated is TRUE

Arguments:

    SendBufferPtr       - pointer to pointer to caller's 16-bit send buffer.
                          We may be able to satisfy the send from this buffer
                          if the data is simple (ie no structures to send). If
                          we have to send structured data then we may have to
                          allocate a new buffer in this routine because we need
                          to move all of the caller's data into one buffer and
                          (s)he may not have allocated enough space to hold
                          everything. Additionally, we cannot assume that we
                          can write the caller's data into their own buffer!

    SendBufLenPtr       - pointer to the length of the allocated buffer. If
                          we allocate a buffer in this routine, this length
                          will alter

    SendBufferAllocated - pointer to a flag which will get set (TRUE) if we do
                          actually allocate a buffer in this routine

    DataDescriptor      - pointer to ASCIZ string which describes the primary
                          data structure in the buffer. This may be updated if
                          NULL pointers are found where a REM_ASCIZ descriptor
                          designates a string pointer

    AuxDescriptor       - pointer to ASCIZ string which describes the secondary
                          data structure in the buffer

    StructureSize       - the size (in bytes) of the fixed portion of the
                          primary data structure

    AuxOffset           - offset to the REM_AUX_NUM descriptor ('N') within the
                          data descriptor, or -1 if there isn't one

    AuxSize             - size in bytes of the fixed part of the secondary data
                          structure, if any

    SetInfoFlag         - indication of whether the API was a SetInfo call

    OkToModifyDescriptor- TRUE if we can modify REM_ASCIZ descriptor chars to
                          REM_NULL_PTR in DataDescriptor, if a NULL pointer is
                          found in the structure. Used by VrNet routines which
                          are not calling VrRemoteApi

Return Value:

    NET_API_STATUS
        Success - NERR_Success
        Failure - ERROR_NOT_ENOUGH_MEMORY
                  NERR_BufTooSmall
--*/

{

    LPBYTE  struct_ptr;
    LPBYTE  c_send_buf;
    LPBYTE  send_ptr;
    DWORD   c_send_len;
    DWORD   buf_length;
    DWORD   to_send_len;
    DWORD   num_aux;
    LPSTR   data_ptr;
    LPSTR   l_dsc;
    LPSTR   l_str;
    BOOL    alloc_flag = FALSE;
    DWORD   num_struct;
    DWORD   len;
    UCHAR   c;
    DWORD   numberOfStructureTypes;
    DWORD   i, j;
    LPBYTE  ptr;

    //
    // Make local copies of the original start and length of the caller's
    // buffer as the originals may change if malloc is used but they
    // will still be needed for the F_RANGE check.
    //

    struct_ptr = c_send_buf = send_ptr = *SendBufferPtr;
    c_send_len = buf_length = *SendBufLenPtr;

    if ((buf_length < StructureSize) || (AuxOffset == StructureSize)) {
        return NERR_BufTooSmall;
    }

    //
    // if the offset to the REM_AUX_NUM descriptor is not -1 then we have
    // associated secondary structures with this primary. The actual number
    // is embedded in the primary structure. Retrieve it
    //

    if (AuxOffset != -1) {
        num_aux = (DWORD)SmbGetUshort((LPWORD)(send_ptr + AuxOffset));
        to_send_len = StructureSize + (num_aux * AuxSize);
        if (buf_length < to_send_len) {
            return NERR_BufTooSmall;
        }
        numberOfStructureTypes = 2;
    } else {
        to_send_len = StructureSize;
        num_aux = AuxSize = 0;
        numberOfStructureTypes = 1;
    }

    //
    // Set up the data pointer to point past fixed length structures
    //

    data_ptr = send_ptr + to_send_len;

    //
    // Any data pointed to by pointers in the data or aux structures
    // must now be copied into the buffer. Start with the primary data
    // structure.
    //

    l_str = DataDescriptor;
    num_struct = 1;         /* Only one primary structure allowed */

    for (i = 0; i < numberOfStructureTypes;
        l_str = AuxDescriptor, num_struct = num_aux, i++) {
        for (j = 0 , l_dsc = l_str; j < num_struct; j++, l_dsc = l_str) {
            for (; (c = *l_dsc) != '\0'; l_dsc++) {
                if (IS_POINTER(c)) {
                    ptr = LPBYTE_FROM_POINTER(struct_ptr);
                    if (ptr == NULL) {
                        if ((*l_dsc == REM_ASCIZ) && OkToModifyDescriptor) {
#ifdef VR_DIAGNOSE
                            DbgPrint("VrpPackSendBuffer: modifying descriptor to REM_NULL_PTR\n");
#endif
                            *l_dsc = REM_NULL_PTR;
                        }
                        struct_ptr += sizeof(LPBYTE);
                        VrpGetArrayLength(l_dsc, &l_dsc);
                    } else {

                        //
                        // If the pointer is NULL or points inside the
                        // original send buffer ( may have been reallocated)
                        // then NULL it as it is not a field being set OR
                        // return an error for a non SetInfo type call as
                        // it is illegal to have a pointer into the
                        // transport buffer.
                        //

                        if (RANGE_F(ptr, c_send_buf, c_send_len)) {
                            if (SetInfoFlag) {
                                SmbPutUlong((LPDWORD)struct_ptr, 0L);
                                VrpGetArrayLength(l_dsc, &l_dsc);
                                struct_ptr += sizeof(LPSTR);
                            } else {
                                return ERROR_INVALID_PARAMETER;
                            }
                        } else {
                            switch (c) {
                            case REM_ASCIZ:
                                len = strlen(ptr) + 1;
                                break;

                            case REM_SEND_LENBUF:
                                len = *(LPWORD)ptr;
                                break;

                            default:
                                len = VrpGetArrayLength(l_dsc, &l_dsc);
                            }

                            //
                            // There is data to be copied into the send
                            // buffer so check that it will fit.
                            //

                            to_send_len += len;
                            if (to_send_len > buf_length) {
                                buf_length = to_send_len + BUF_INC;
                                if (!alloc_flag) {

                                    //
                                    // Need new buffer
                                    //

                                    send_ptr = (LPBYTE)LocalAlloc(LMEM_FIXED, buf_length);
                                    if (send_ptr == NULL) {
                                        return ERROR_NOT_ENOUGH_MEMORY;
                                    }
                                    alloc_flag = TRUE;

                                    //
                                    // Got new buffer, so copy old buffer
                                    //

                                    RtlCopyMemory(send_ptr, c_send_buf, to_send_len - len);
                                    struct_ptr = send_ptr + (struct_ptr - c_send_buf);
                                    data_ptr = send_ptr + (data_ptr - c_send_buf);
                                } else {
                                    LPBYTE  newPtr;

                                    newPtr = (LPBYTE)LocalReAlloc(send_ptr, buf_length, LMEM_MOVEABLE);
                                    if (newPtr == NULL) {
                                        LocalFree(send_ptr);
                                        return ERROR_NOT_ENOUGH_MEMORY;
                                    } else if (newPtr != send_ptr) {

                                        //
                                        // fix up the pointers
                                        //

                                        data_ptr = newPtr + (data_ptr - send_ptr);
                                        struct_ptr = newPtr + (struct_ptr - send_ptr);
                                        send_ptr = newPtr;
                                    }
                                }
                            }

                            //
                            // There is room for new data in buffer so copy
                            // it and and update the struct and data ptrs
                            //

                            RtlCopyMemory(data_ptr, ptr, len);
                            data_ptr += len;
                            struct_ptr += sizeof(LPBYTE);
                        }
                    }
                } else {

                    //
                    // If the descriptor was not a pointer type then step
                    // over the corresponding data field.
                    //

                    struct_ptr += VrpGetFieldSize(l_dsc, &l_dsc);
                }
            }
        }
    }

    *SendBufferPtr = send_ptr;

    //
    // Note that this is potentially incorrect: we are actually returning the
    // size of the structure + dynamic data to be sent, which is probably not
    // the same as the size of the buffer we (re)allocated. This is how it is
    // done in Lanman, so we'll do the same thing until it breaks
    //

    *SendBufLenPtr = to_send_len;
    *SendBufferAllocated = alloc_flag;

    return NERR_Success;
}