1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright (c) 1991, 1999 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26 /*
27 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
28 */
29
30 #include <sys/types.h>
31 #include <sys/errno.h>
32 #include <setjmp.h>
33 #include <sys/tiuser.h>
34 #include <string.h>
35
36 #include <rpc/types.h>
37 #include <rpc/xdr.h>
38 #include <rpc/auth.h>
39 #include <rpc/clnt.h>
40 #include <rpc/rpc_msg.h>
41 #include <rpc/pmap_prot.h>
42 #include "snoop.h"
43
44 /*
45 * Number of bytes to display from a string (address, netid, etc.).
46 */
47 #define MAXSTRINGLEN 64
48
49 extern char *dlc_header;
50 extern jmp_buf xdr_err;
51
52 static void interpret_pmap_2(int, int, int, int, int, char *, int);
53 static void interpret_pmap_4(int, int, int, int, int, char *, int);
54 static void stash_callit(ulong_t, int, int, int, int);
55
56 void
interpret_pmap(flags,type,xid,vers,proc,data,len)57 interpret_pmap(flags, type, xid, vers, proc, data, len)
58 int flags, type, xid, vers, proc;
59 char *data;
60 int len;
61 {
62 switch (vers) {
63 case 2: interpret_pmap_2(flags, type, xid, vers, proc, data, len);
64 break;
65
66 /* Version 3 is a subset of version 4 */
67 case 3:
68 case 4: interpret_pmap_4(flags, type, xid, vers, proc, data, len);
69 break;
70 }
71 }
72
73 void show_pmap();
74 char *sum_pmaplist();
75 void show_pmaplist();
76
77 static char *procnames_short_2[] = {
78 "Null", /* 0 */
79 "SET", /* 1 */
80 "UNSET", /* 2 */
81 "GETPORT", /* 3 */
82 "DUMP", /* 4 */
83 "CALLIT", /* 5 */
84 };
85
86 static char *procnames_long_2[] = {
87 "Null procedure", /* 0 */
88 "Set port", /* 1 */
89 "Unset port", /* 2 */
90 "Get port number", /* 3 */
91 "Dump the mappings", /* 4 */
92 "Indirect call", /* 5 */
93 };
94
95 #define MAXPROC_2 5
96
97 void
interpret_pmap_2(flags,type,xid,vers,proc,data,len)98 interpret_pmap_2(flags, type, xid, vers, proc, data, len)
99 int flags, type, xid, vers, proc;
100 char *data;
101 int len;
102 {
103 char *line;
104 unsigned port, proto;
105 unsigned iprog, ivers, iproc, ilen;
106 extern int pi_frame;
107 struct cache_struct *x, *find_callit();
108 int trailer_done = 0;
109
110 if (proc < 0 || proc > MAXPROC_2)
111 return;
112
113 if (proc == PMAPPROC_CALLIT) {
114 if (type == CALL) {
115 iprog = getxdr_u_long();
116 ivers = getxdr_u_long();
117 iproc = getxdr_u_long();
118 stash_callit(xid, pi_frame, iprog, ivers, iproc);
119 } else {
120 x = find_callit(xid);
121 }
122 }
123
124 if (flags & F_SUM) {
125 if (setjmp(xdr_err)) {
126 return;
127 }
128
129 line = get_sum_line();
130
131 if (type == CALL) {
132 (void) sprintf(line, "PORTMAP C %s",
133 procnames_short_2[proc]);
134 line += strlen(line);
135 switch (proc) {
136 case PMAPPROC_GETPORT:
137 iprog = getxdr_u_long();
138 ivers = getxdr_u_long();
139 proto = getxdr_u_long();
140 (void) sprintf(line,
141 " prog=%d (%s) vers=%d proto=%s",
142 iprog, nameof_prog(iprog),
143 ivers,
144 getproto(proto));
145 break;
146 case PMAPPROC_CALLIT:
147 (void) sprintf(line,
148 " prog=%s vers=%d proc=%d",
149 nameof_prog(iprog),
150 ivers, iproc);
151 if (flags & F_ALLSUM) {
152 (void) getxdr_u_long(); /* length */
153 data += 16; /* prog+ver+proc+len */
154 len -= 16;
155 protoprint(flags, type, xid,
156 iprog, ivers, iproc,
157 data, len);
158 }
159 break;
160 default:
161 break;
162 }
163 check_retransmit(line, xid);
164 } else {
165 (void) sprintf(line, "PORTMAP R %s ",
166 procnames_short_2[proc]);
167 line += strlen(line);
168 switch (proc) {
169 case PMAPPROC_GETPORT:
170 port = getxdr_u_long();
171 (void) sprintf(line, "port=%d", port);
172 break;
173 case PMAPPROC_DUMP:
174 (void) sprintf(line, "%s", sum_pmaplist());
175 break;
176 case PMAPPROC_CALLIT:
177 port = getxdr_u_long();
178 ilen = getxdr_u_long();
179 (void) sprintf(line, "port=%d len=%d",
180 port, ilen);
181 if (flags & F_ALLSUM && x != NULL) {
182 data += 8; /* port+len */
183 len -= 8;
184 protoprint(flags, type, xid,
185 x->xid_prog,
186 x->xid_vers,
187 x->xid_proc,
188 data, len);
189 }
190 break;
191 default:
192 break;
193 }
194 }
195 }
196
197 if (flags & F_DTAIL) {
198 show_header("PMAP: ", "Portmapper", len);
199 show_space();
200 if (setjmp(xdr_err)) {
201 return;
202 }
203 (void) sprintf(get_line(0, 0),
204 "Proc = %d (%s)",
205 proc, procnames_long_2[proc]);
206 if (type == CALL) {
207 switch (proc) {
208 case PMAPPROC_NULL:
209 case PMAPPROC_SET:
210 case PMAPPROC_UNSET:
211 break;
212 case PMAPPROC_GETPORT:
213 iprog = getxdr_u_long();
214 (void) sprintf(get_line(0, 0),
215 "Program = %d (%s)",
216 iprog, nameof_prog(iprog));
217 (void) showxdr_u_long("Version = %d");
218 proto = getxdr_u_long();
219 (void) sprintf(get_line(0, 0),
220 "Protocol = %d (%s)",
221 proto, getproto(proto));
222 break;
223 case PMAPPROC_DUMP:
224 break;
225 case PMAPPROC_CALLIT:
226 (void) sprintf(get_line(0, 0),
227 "Program = %d (%s)",
228 iprog, nameof_prog(iprog));
229 (void) sprintf(get_line(0, 0),
230 "Version = %d", ivers);
231 (void) sprintf(get_line(0, 0),
232 "Proc = %d", iproc);
233 (void) showxdr_u_long("Callit data = %d bytes");
234 show_trailer();
235 trailer_done = 1;
236 data += 16; /* prog+ver+proc+len */
237 len -= 16;
238 protoprint(flags, type, xid,
239 iprog, ivers, iproc,
240 data, len);
241 break;
242 }
243 } else {
244 switch (proc) {
245 case PMAPPROC_NULL:
246 case PMAPPROC_SET:
247 case PMAPPROC_UNSET:
248 break;
249 case PMAPPROC_GETPORT:
250 (void) showxdr_u_long("Port = %d");
251 break;
252 case PMAPPROC_DUMP:
253 show_pmaplist();
254 break;
255 case PMAPPROC_CALLIT:
256 (void) showxdr_u_long("Port = %d");
257 (void) showxdr_u_long("Length = %d bytes");
258 show_trailer();
259 trailer_done = 1;
260 if (x != NULL) {
261 protoprint(flags, type, xid,
262 x->xid_prog,
263 x->xid_vers,
264 x->xid_proc,
265 data, len);
266 }
267 break;
268 }
269 }
270 if (!trailer_done)
271 show_trailer();
272 }
273 }
274
275 char *
sum_pmaplist()276 sum_pmaplist()
277 {
278 int maps = 0;
279 static char buff[16];
280
281 if (setjmp(xdr_err)) {
282 (void) sprintf(buff, "%d+ map(s) found", maps);
283 return (buff);
284 }
285
286 while (getxdr_u_long()) {
287 (void) getxdr_u_long(); /* program */
288 (void) getxdr_u_long(); /* version */
289 (void) getxdr_u_long(); /* protocol */
290 (void) getxdr_u_long(); /* port */
291 maps++;
292 }
293
294 (void) sprintf(buff, "%d map(s) found", maps);
295 return (buff);
296 }
297
298 void
show_pmaplist()299 show_pmaplist()
300 {
301 unsigned prog, vers, proto, port;
302 int maps = 0;
303
304 if (setjmp(xdr_err)) {
305 (void) sprintf(get_line(0, 0),
306 " %d+ maps. (Frame is incomplete)",
307 maps);
308 return;
309 }
310
311 (void) sprintf(get_line(0, 0), " Program Version Protocol Port");
312
313 while (getxdr_u_long()) {
314 prog = getxdr_u_long();
315 vers = getxdr_u_long();
316 proto = getxdr_u_long();
317 port = getxdr_u_long();
318 (void) sprintf(get_line(0, 0),
319 "%8d%8d%9d%7d %s",
320 prog, vers, proto, port, nameof_prog(prog));
321 maps++;
322 }
323
324 (void) sprintf(get_line(0, 0), " %d maps", maps);
325 }
326
327 /*
328 * ******************************************
329 */
330 char *sum_rpcblist();
331 void show_rpcblist();
332 char *sum_rpcb_entry_list();
333 void show_rpcb_entry_list();
334
335 static char *procnames_short_4[] = {
336 /*
337 * version 3 and 4 procs
338 */
339 "Null", /* 0 */
340 "SET", /* 1 */
341 "UNSET", /* 2 */
342 "GETADDR", /* 3 */
343 "DUMP", /* 4 */
344 "BCAST", /* 5 */
345 "GETTIME", /* 6 */
346 "UADDR2TADDR", /* 7 */
347 "TADDR2UADDR", /* 8 */
348 /*
349 * version 4 procs only
350 */
351 "GETVERSADDR", /* 9 */
352 "INDIRECT", /* 10 */
353 "GETADDRLIST", /* 11 */
354 "GETSTAT", /* 12 */
355 };
356
357 static char *procnames_long_4[] = {
358 /*
359 * version 3 and 4 procs
360 */
361 "Null procedure", /* 0 */
362 "Set address", /* 1 */
363 "Unset address", /* 2 */
364 "Get address", /* 3 */
365 "Dump the mappings", /* 4 */
366 "Broadcast call (no error)", /* 5 */
367 "Get the time", /* 6 */
368 "Universal to transport address", /* 7 */
369 "Transport to universal address", /* 8 */
370 /*
371 * version 4 procs only
372 */
373 "Get address of specific version", /* 9 */
374 "Indirect call (return error)", /* 10 */
375 "Return addresses of prog/vers", /* 11 */
376 "Get statistics", /* 12 */
377 };
378
379 #define MAXPROC_3 8
380 #define MAXPROC_4 12
381 #define RPCBPROC_NULL 0
382
383 void
interpret_pmap_4(flags,type,xid,vers,proc,data,len)384 interpret_pmap_4(flags, type, xid, vers, proc, data, len)
385 int flags, type, xid, vers, proc;
386 char *data;
387 int len;
388 {
389 char *line;
390 unsigned prog, ver;
391 char buff1[MAXSTRINGLEN + 1];
392 int iprog, ivers, iproc, ilen;
393 extern int pi_frame;
394 struct cache_struct *x, *find_callit();
395 int trailer_done = 0;
396
397 if (proc < 0 || proc > MAXPROC_4 || (vers == 3 && proc > MAXPROC_3))
398 return;
399
400 if (proc == RPCBPROC_BCAST || proc == RPCBPROC_INDIRECT) {
401 if (type == CALL) {
402 iprog = getxdr_u_long();
403 ivers = getxdr_u_long();
404 iproc = getxdr_u_long();
405 stash_callit(xid, pi_frame,
406 iprog, ivers, iproc);
407 } else {
408 x = find_callit(xid);
409 }
410 }
411
412 if (flags & F_SUM) {
413 if (setjmp(xdr_err)) {
414 return;
415 }
416
417 line = get_sum_line();
418
419 if (type == CALL) {
420 (void) sprintf(line,
421 "RPCBIND C %s",
422 procnames_short_4[proc]);
423 line += strlen(line);
424 switch (proc) {
425 case RPCBPROC_SET:
426 case RPCBPROC_UNSET:
427 case RPCBPROC_GETADDR:
428 case RPCBPROC_GETVERSADDR:
429 case RPCBPROC_GETADDRLIST:
430 prog = getxdr_u_long();
431 ver = getxdr_u_long();
432 (void) sprintf(line,
433 " prog=%d (%s) vers=%d",
434 prog, nameof_prog(prog),
435 ver);
436 break;
437 case RPCBPROC_BCAST:
438 case RPCBPROC_INDIRECT:
439 (void) sprintf(line,
440 " prog=%s vers=%d proc=%d",
441 nameof_prog(iprog),
442 ivers, iproc);
443 if (flags & F_ALLSUM) {
444 (void) getxdr_u_long(); /* length */
445 data += 16; /* prog+ver+proc+len */
446 len -= 16;
447 protoprint(flags, type, xid,
448 iprog, ivers, iproc,
449 data, len);
450 }
451 break;
452 default:
453 break;
454 }
455
456 check_retransmit(line, xid);
457 } else {
458 int pos;
459
460 (void) sprintf(line, "RPCBIND R %s ",
461 procnames_short_4[proc]);
462 line += strlen(line);
463 switch (proc) {
464 case RPCBPROC_GETADDR:
465 case RPCBPROC_TADDR2UADDR:
466 case RPCBPROC_GETVERSADDR:
467 (void) getxdr_string(buff1, MAXSTRINGLEN);
468 (void) sprintf(line,
469 " Uaddr=%s",
470 buff1);
471 break;
472 case RPCBPROC_BCAST:
473 case RPCBPROC_INDIRECT:
474 pos = getxdr_pos();
475 (void) getxdr_string(buff1, MAXSTRINGLEN);
476 ilen = getxdr_u_long();
477 (void) sprintf(line, "Uaddr=%s len=%d",
478 buff1, ilen);
479 if (flags & F_ALLSUM && x != NULL) {
480 pos = getxdr_pos() - pos;
481 data += pos; /* uaddr+len */
482 len -= pos;
483 protoprint(flags, type, xid,
484 x->xid_prog,
485 x->xid_vers,
486 x->xid_proc,
487 data, len);
488 }
489 break;
490 case RPCBPROC_DUMP:
491 (void) sprintf(line, "%s",
492 sum_rpcblist());
493 break;
494 case RPCBPROC_GETTIME:
495 {
496 time_t sec = getxdr_long();
497 struct tm *tmp = gmtime(&sec);
498 (void) strftime(line, MAXLINE,
499 "%d-%h-%y %T GMT", tmp);
500 }
501 break;
502 case RPCBPROC_GETADDRLIST:
503 (void) sprintf(line, "%s",
504 sum_rpcb_entry_list());
505 break;
506 default:
507 break;
508 }
509 }
510 }
511
512 if (flags & F_DTAIL) {
513 show_header("RPCB: ", "RPC Bind", len);
514 show_space();
515 if (setjmp(xdr_err)) {
516 return;
517 }
518 (void) sprintf(get_line(0, 0),
519 "Proc = %d (%s)",
520 proc, procnames_long_4[proc]);
521 if (type == CALL) {
522 switch (proc) {
523 case RPCBPROC_NULL:
524 break;
525 case RPCBPROC_SET:
526 case RPCBPROC_UNSET:
527 case RPCBPROC_GETADDR:
528 case RPCBPROC_GETVERSADDR:
529 case RPCBPROC_GETADDRLIST:
530 (void) showxdr_u_long("Program = %d");
531 (void) showxdr_u_long("Version = %d");
532 (void) showxdr_string(64, "Netid = %s");
533 break;
534 case RPCBPROC_DUMP:
535 break;
536 case RPCBPROC_BCAST:
537 case RPCBPROC_INDIRECT:
538 (void) sprintf(get_line(0, 0),
539 "Program = %d (%s)",
540 iprog, nameof_prog(iprog));
541 (void) sprintf(get_line(0, 0),
542 "Version = %d", ivers);
543 (void) sprintf(get_line(0, 0),
544 "Proc = %d", iproc);
545 (void) showxdr_u_long(
546 "Callit data = %d bytes");
547 show_trailer();
548 trailer_done = 1;
549 data += 16; /* prog+ver+proc+len */
550 len -= 16;
551 protoprint(flags, type, xid,
552 iprog, ivers, iproc,
553 data, len);
554 break;
555 case RPCBPROC_GETTIME:
556 break;
557 case RPCBPROC_UADDR2TADDR:
558 case RPCBPROC_TADDR2UADDR:
559 break;
560 }
561 } else {
562 switch (proc) {
563 case RPCBPROC_NULL:
564 case RPCBPROC_SET:
565 case RPCBPROC_UNSET:
566 break;
567 case RPCBPROC_GETADDR:
568 case RPCBPROC_TADDR2UADDR:
569 case RPCBPROC_GETVERSADDR:
570 (void) showxdr_string(64, "Uaddr = %s");
571 break;
572 case RPCBPROC_DUMP:
573 show_rpcblist();
574 break;
575 case RPCBPROC_BCAST:
576 case RPCBPROC_INDIRECT:
577 (void) showxdr_string(64, "Uaddr = %s");
578 (void) showxdr_u_long("Length = %d bytes");
579 show_trailer();
580 trailer_done = 1;
581 if (x != NULL) {
582 protoprint(flags, type, xid,
583 x->xid_prog,
584 x->xid_vers,
585 x->xid_proc,
586 data, len);
587 }
588 break;
589 case RPCBPROC_GETTIME:
590 {
591 int pos = getxdr_pos();
592 time_t sec = getxdr_long();
593 struct tm *tmp = gmtime(&sec);
594 (void) strftime(get_line(pos,
595 getxdr_pos()), MAXLINE,
596 "Time = %d-%h-%y %T GMT", tmp);
597 }
598 break;
599 case RPCBPROC_UADDR2TADDR:
600 break;
601 case RPCBPROC_GETADDRLIST:
602 show_rpcb_entry_list();
603 break;
604 }
605 }
606 if (!trailer_done)
607 show_trailer();
608 }
609 }
610
611 char *
sum_rpcblist()612 sum_rpcblist()
613 {
614 int maps = 0;
615 static char buff[MAXSTRINGLEN + 1];
616
617 if (setjmp(xdr_err)) {
618 (void) sprintf(buff, "%d+ map(s) found", maps);
619 return (buff);
620 }
621
622 while (getxdr_u_long()) {
623 (void) getxdr_u_long(); /* program */
624 (void) getxdr_u_long(); /* version */
625 (void) getxdr_string(buff, MAXSTRINGLEN); /* netid */
626 (void) getxdr_string(buff, MAXSTRINGLEN); /* uaddr */
627 (void) getxdr_string(buff, MAXSTRINGLEN); /* owner */
628 maps++;
629 }
630
631 (void) sprintf(buff, "%d map(s) found", maps);
632 return (buff);
633 }
634
635 void
show_rpcblist()636 show_rpcblist()
637 {
638 unsigned prog, vers;
639 char netid[MAXSTRINGLEN + 1], uaddr[MAXSTRINGLEN + 1];
640 char owner[MAXSTRINGLEN + 1];
641 int maps = 0;
642
643 if (setjmp(xdr_err)) {
644 (void) sprintf(get_line(0, 0),
645 " %d+ maps. (Frame is incomplete)",
646 maps);
647 return;
648 }
649
650 show_space();
651 (void) sprintf(get_line(0, 0),
652 " Program Vers Netid Uaddr Owner");
653
654 while (getxdr_u_long()) {
655 prog = getxdr_u_long();
656 vers = getxdr_u_long();
657 (void) getxdr_string(netid, MAXSTRINGLEN);
658 (void) getxdr_string(uaddr, MAXSTRINGLEN);
659 (void) getxdr_string(owner, MAXSTRINGLEN);
660 (void) sprintf(get_line(0, 0),
661 "%8d%5d %-12s %-18s %-10s (%s)",
662 prog, vers,
663 netid, uaddr, owner,
664 nameof_prog(prog));
665 maps++;
666 }
667
668 (void) sprintf(get_line(0, 0), " (%d maps)", maps);
669 }
670
671 char *
sum_rpcb_entry_list()672 sum_rpcb_entry_list()
673 {
674 int maps = 0;
675 static char buff[MAXSTRINGLEN + 1];
676
677 if (setjmp(xdr_err)) {
678 (void) sprintf(buff, "%d+ map(s) found", maps);
679 return (buff);
680 }
681
682 while (getxdr_u_long()) {
683 (void) getxdr_string(buff, MAXSTRINGLEN); /* maddr */
684 (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_netid */
685 (void) getxdr_u_long(); /* nc_semantics */
686 (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_protofmly */
687 (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_proto */
688 maps++;
689 }
690
691 (void) sprintf(buff, "%d map(s) found", maps);
692 return (buff);
693 }
694
695 char *semantics_strs[] = {"", "CLTS", "COTS", "COTS-ORD", "RAW"};
696
697 void
show_rpcb_entry_list()698 show_rpcb_entry_list()
699 {
700 char maddr[MAXSTRINGLEN + 1], netid[MAXSTRINGLEN + 1];
701 char protofmly[MAXSTRINGLEN + 1], proto[MAXSTRINGLEN + 1];
702 unsigned sem;
703 int maps = 0;
704
705 if (setjmp(xdr_err)) {
706 (void) sprintf(get_line(0, 0),
707 " %d+ maps. (Frame is incomplete)",
708 maps);
709 return;
710 }
711
712 show_space();
713 (void) sprintf(get_line(0, 0),
714 " Maddr Netid Semantics Protofmly Proto");
715
716 while (getxdr_u_long()) {
717 (void) getxdr_string(maddr, MAXSTRINGLEN);
718 (void) getxdr_string(netid, MAXSTRINGLEN);
719 sem = getxdr_u_long();
720 (void) getxdr_string(protofmly, MAXSTRINGLEN);
721 (void) getxdr_string(proto, MAXSTRINGLEN);
722 (void) sprintf(get_line(0, 0),
723 "%-12s %-12s %-8s %-8s %-8s",
724 maddr, netid,
725 semantics_strs[sem],
726 protofmly, proto);
727 maps++;
728 }
729
730 (void) sprintf(get_line(0, 0), " (%d maps)", maps);
731 }
732
733 #define CXID_CACHE_SIZE 16
734 struct cache_struct cxid_cache[CXID_CACHE_SIZE];
735 struct cache_struct *cxcpfirst = &cxid_cache[0];
736 struct cache_struct *cxcp = &cxid_cache[0];
737 struct cache_struct *cxcplast = &cxid_cache[CXID_CACHE_SIZE - 1];
738
739 struct cache_struct *
find_callit(xid)740 find_callit(xid)
741 ulong_t xid;
742 {
743 struct cache_struct *x;
744
745 for (x = cxcp; x >= cxcpfirst; x--)
746 if (x->xid_num == xid)
747 return (x);
748 for (x = cxcplast; x > cxcp; x--)
749 if (x->xid_num == xid)
750 return (x);
751 return (NULL);
752 }
753
754 static void
stash_callit(xid,frame,prog,vers,proc)755 stash_callit(xid, frame, prog, vers, proc)
756 ulong_t xid;
757 int frame, prog, vers, proc;
758 {
759 struct cache_struct *x;
760
761 x = find_callit(xid);
762 if (x == NULL) {
763 x = cxcp++;
764 if (cxcp > cxcplast)
765 cxcp = cxcpfirst;
766 x->xid_num = xid;
767 x->xid_frame = frame;
768 }
769 x->xid_prog = prog;
770 x->xid_vers = vers;
771 x->xid_proc = proc;
772 }
773