]> git.ipfire.org Git - thirdparty/glibc.git/blame - sunrpc/clnt_unix.c
elf: Refuse to dlopen PIE objects [BZ #24323]
[thirdparty/glibc.git] / sunrpc / clnt_unix.c
CommitLineData
e852e889 1/*
ab09b221 2 * clnt_unix.c, Implements a TCP/IP based, client side RPC.
e852e889 3 *
a7ab6ec8
UD
4 * Copyright (c) 2010, Oracle America, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
16 * * Neither the name of the "Oracle America, Inc." nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
e852e889 32 *
e852e889
UD
33 * TCP based RPC supports 'batched calls'.
34 * A sequence of calls may be batched-up in a send buffer. The rpc call
35 * return immediately to the client even though the call was not necessarily
36 * sent. The batching occurs if the results' xdr routine is NULL (0) AND
37 * the rpc timeout value is zero (see clnt.h, rpc).
38 *
39 * Clients should NOT casually batch calls that in fact return results; that is,
40 * the server side should be aware that a call is batched and not produce any
41 * return message. Batched calls that produce many result messages can
42 * deadlock (netlock) the client and the server....
43 *
44 * Now go hang yourself.
45 */
46
47#include <netdb.h>
48#include <errno.h>
49#include <stdio.h>
50#include <unistd.h>
4360eafd 51#include <libintl.h>
e852e889
UD
52#include <rpc/rpc.h>
53#include <sys/uio.h>
54#include <sys/poll.h>
55#include <sys/socket.h>
56#include <rpc/pmap_clnt.h>
3ce1f295 57#include <wchar.h>
82f43dd2 58#include <shlib-compat.h>
e852e889 59
090ca000
UD
60extern u_long _create_xid (void);
61
e852e889
UD
62#define MCALL_MSG_SIZE 24
63
64struct ct_data
65 {
66 int ct_sock;
67 bool_t ct_closeit;
68 struct timeval ct_wait;
69 bool_t ct_waitset; /* wait set by clnt_control? */
70 struct sockaddr_un ct_addr;
71 struct rpc_err ct_error;
72 char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */
73 u_int ct_mpos; /* pos after marshal */
74 XDR ct_xdrs;
75 };
76
77static int readunix (char *, char *, int);
78static int writeunix (char *, char *, int);
79
80static enum clnt_stat clntunix_call (CLIENT *, u_long, xdrproc_t, caddr_t,
81 xdrproc_t, caddr_t, struct timeval);
82static void clntunix_abort (void);
83static void clntunix_geterr (CLIENT *, struct rpc_err *);
84static bool_t clntunix_freeres (CLIENT *, xdrproc_t, caddr_t);
85static bool_t clntunix_control (CLIENT *, int, char *);
86static void clntunix_destroy (CLIENT *);
87
31d7b14c 88static const struct clnt_ops unix_ops =
e852e889
UD
89{
90 clntunix_call,
91 clntunix_abort,
92 clntunix_geterr,
93 clntunix_freeres,
94 clntunix_destroy,
95 clntunix_control
96};
97
98/*
99 * Create a client handle for a tcp/ip connection.
100 * If *sockp<0, *sockp is set to a newly created TCP socket and it is
101 * connected to raddr. If *sockp non-negative then
102 * raddr is ignored. The rpc/tcp package does buffering
103 * similar to stdio, so the client must pick send and receive buffer sizes,];
104 * 0 => use the default.
105 * If raddr->sin_port is 0, then a binder on the remote machine is
106 * consulted for the right port number.
107 * NB: *sockp is copied into a private area.
108 * NB: It is the clients responsibility to close *sockp.
109 * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this
110 * something more useful.
111 */
112CLIENT *
113clntunix_create (struct sockaddr_un *raddr, u_long prog, u_long vers,
114 int *sockp, u_int sendsz, u_int recvsz)
115{
116 CLIENT *h;
117 struct ct_data *ct = (struct ct_data *) mem_alloc (sizeof (*ct));
e852e889
UD
118 struct rpc_msg call_msg;
119 int len;
120
121 h = (CLIENT *) mem_alloc (sizeof (*h));
51028f34 122 if (h == NULL || ct == NULL)
e852e889 123 {
543cf8a9 124 struct rpc_createerr *ce = &get_rpc_createerr ();
1d20f7f8 125 (void) __fxprintf (NULL, "%s: %s", __func__, _("out of memory\n"));
543cf8a9 126 ce->cf_stat = RPC_SYSTEMERROR;
51028f34 127 ce->cf_error.re_errno = ENOMEM;
e852e889
UD
128 goto fooy;
129 }
130
131 /*
132 * If no socket given, open one
133 */
134 if (*sockp < 0)
135 {
136 *sockp = __socket (AF_UNIX, SOCK_STREAM, 0);
137 len = strlen (raddr->sun_path) + sizeof (raddr->sun_family) + 1;
138 if (*sockp < 0
139 || __connect (*sockp, (struct sockaddr *) raddr, len) < 0)
140 {
543cf8a9
UD
141 struct rpc_createerr *ce = &get_rpc_createerr ();
142 ce->cf_stat = RPC_SYSTEMERROR;
143 ce->cf_error.re_errno = errno;
e852e889
UD
144 if (*sockp != -1)
145 __close (*sockp);
146 goto fooy;
147 }
148 ct->ct_closeit = TRUE;
149 }
150 else
151 {
152 ct->ct_closeit = FALSE;
153 }
154
155 /*
156 * Set up private data struct
157 */
158 ct->ct_sock = *sockp;
159 ct->ct_wait.tv_usec = 0;
160 ct->ct_waitset = FALSE;
161 ct->ct_addr = *raddr;
162
163 /*
164 * Initialize call message
165 */
090ca000 166 call_msg.rm_xid = _create_xid ();
e852e889
UD
167 call_msg.rm_direction = CALL;
168 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
169 call_msg.rm_call.cb_prog = prog;
170 call_msg.rm_call.cb_vers = vers;
171
172 /*
173 * pre-serialize the static part of the call msg and stash it away
174 */
7b57bfe5
UD
175 xdrmem_create (&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE, XDR_ENCODE);
176 if (!xdr_callhdr (&(ct->ct_xdrs), &call_msg))
e852e889
UD
177 {
178 if (ct->ct_closeit)
179 __close (*sockp);
180 goto fooy;
181 }
182 ct->ct_mpos = XDR_GETPOS (&(ct->ct_xdrs));
183 XDR_DESTROY (&(ct->ct_xdrs));
184
185 /*
186 * Create a client handle which uses xdrrec for serialization
187 * and authnone for authentication.
188 */
7b57bfe5
UD
189 xdrrec_create (&(ct->ct_xdrs), sendsz, recvsz,
190 (caddr_t) ct, readunix, writeunix);
31d7b14c 191 h->cl_ops = (struct clnt_ops *) &unix_ops;
e852e889 192 h->cl_private = (caddr_t) ct;
7b57bfe5 193 h->cl_auth = authnone_create ();
e852e889
UD
194 return h;
195
196fooy:
197 /*
198 * Something goofed, free stuff and barf
199 */
200 mem_free ((caddr_t) ct, sizeof (struct ct_data));
201 mem_free ((caddr_t) h, sizeof (CLIENT));
202 return (CLIENT *) NULL;
203}
021db4be 204libc_hidden_nolink_sunrpc (clntunix_create, GLIBC_2_1)
e852e889
UD
205
206static enum clnt_stat
f63f2bfd
JM
207clntunix_call (CLIENT *h, u_long proc, xdrproc_t xdr_args, caddr_t args_ptr,
208 xdrproc_t xdr_results, caddr_t results_ptr,
209 struct timeval timeout)
e852e889
UD
210{
211 struct ct_data *ct = (struct ct_data *) h->cl_private;
212 XDR *xdrs = &(ct->ct_xdrs);
213 struct rpc_msg reply_msg;
214 u_long x_id;
d9fee042 215 uint32_t *msg_x_id = (uint32_t *) (ct->ct_mcall); /* yuk */
e852e889
UD
216 bool_t shipnow;
217 int refreshes = 2;
218
219 if (!ct->ct_waitset)
220 {
221 ct->ct_wait = timeout;
222 }
223
224 shipnow =
a69a8d9c
UD
225 (xdr_results == (xdrproc_t) 0 && ct->ct_wait.tv_sec == 0
226 && ct->ct_wait.tv_usec == 0) ? FALSE : TRUE;
e852e889
UD
227
228call_again:
229 xdrs->x_op = XDR_ENCODE;
230 ct->ct_error.re_status = RPC_SUCCESS;
231 x_id = ntohl (--(*msg_x_id));
232 if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) ||
233 (!XDR_PUTLONG (xdrs, (long *) &proc)) ||
234 (!AUTH_MARSHALL (h->cl_auth, xdrs)) ||
235 (!(*xdr_args) (xdrs, args_ptr)))
236 {
237 if (ct->ct_error.re_status == RPC_SUCCESS)
238 ct->ct_error.re_status = RPC_CANTENCODEARGS;
7b57bfe5 239 (void) xdrrec_endofrecord (xdrs, TRUE);
e852e889
UD
240 return ct->ct_error.re_status;
241 }
7b57bfe5 242 if (!xdrrec_endofrecord (xdrs, shipnow))
e852e889
UD
243 return ct->ct_error.re_status = RPC_CANTSEND;
244 if (!shipnow)
245 return RPC_SUCCESS;
246 /*
247 * Hack to provide rpc-based message passing
248 */
a69a8d9c 249 if (ct->ct_wait.tv_sec == 0 && ct->ct_wait.tv_usec == 0)
e852e889
UD
250 return ct->ct_error.re_status = RPC_TIMEDOUT;
251
252
253 /*
254 * Keep receiving until we get a valid transaction id
255 */
256 xdrs->x_op = XDR_DECODE;
257 while (TRUE)
258 {
259 reply_msg.acpted_rply.ar_verf = _null_auth;
260 reply_msg.acpted_rply.ar_results.where = NULL;
7b57bfe5
UD
261 reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
262 if (!xdrrec_skiprecord (xdrs))
e852e889
UD
263 return ct->ct_error.re_status;
264 /* now decode and validate the response header */
7b57bfe5 265 if (!xdr_replymsg (xdrs, &reply_msg))
e852e889
UD
266 {
267 if (ct->ct_error.re_status == RPC_SUCCESS)
268 continue;
269 return ct->ct_error.re_status;
270 }
271 if (reply_msg.rm_xid == x_id)
272 break;
273 }
274
275 /*
276 * process header
277 */
278 _seterr_reply (&reply_msg, &(ct->ct_error));
279 if (ct->ct_error.re_status == RPC_SUCCESS)
280 {
281 if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf))
282 {
283 ct->ct_error.re_status = RPC_AUTHERROR;
284 ct->ct_error.re_why = AUTH_INVALIDRESP;
285 }
286 else if (!(*xdr_results) (xdrs, results_ptr))
287 {
288 if (ct->ct_error.re_status == RPC_SUCCESS)
289 ct->ct_error.re_status = RPC_CANTDECODERES;
290 }
291 /* free verifier ... */
292 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
293 {
294 xdrs->x_op = XDR_FREE;
7b57bfe5 295 (void) xdr_opaque_auth (xdrs, &(reply_msg.acpted_rply.ar_verf));
e852e889
UD
296 }
297 } /* end successful completion */
298 else
299 {
300 /* maybe our credentials need to be refreshed ... */
301 if (refreshes-- && AUTH_REFRESH (h->cl_auth))
302 goto call_again;
303 } /* end of unsuccessful completion */
304 return ct->ct_error.re_status;
305}
306
307static void
308clntunix_geterr (CLIENT *h, struct rpc_err *errp)
309{
310 struct ct_data *ct = (struct ct_data *) h->cl_private;
311
312 *errp = ct->ct_error;
313}
314
315static bool_t
9d46370c 316clntunix_freeres (CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr)
e852e889
UD
317{
318 struct ct_data *ct = (struct ct_data *) cl->cl_private;
319 XDR *xdrs = &(ct->ct_xdrs);
320
321 xdrs->x_op = XDR_FREE;
322 return (*xdr_res) (xdrs, res_ptr);
323}
324
325static void
60d2f8f3 326clntunix_abort (void)
e852e889
UD
327{
328}
329
330static bool_t
331clntunix_control (CLIENT *cl, int request, char *info)
332{
333 struct ct_data *ct = (struct ct_data *) cl->cl_private;
4efbd5cb 334 u_long ul;
d9fee042 335 uint32_t ui32;
e852e889
UD
336
337 switch (request)
338 {
339 case CLSET_FD_CLOSE:
340 ct->ct_closeit = TRUE;
341 break;
342 case CLSET_FD_NCLOSE:
343 ct->ct_closeit = FALSE;
344 break;
345 case CLSET_TIMEOUT:
346 ct->ct_wait = *(struct timeval *) info;
347 break;
348 case CLGET_TIMEOUT:
349 *(struct timeval *) info = ct->ct_wait;
350 break;
351 case CLGET_SERVER_ADDR:
352 *(struct sockaddr_un *) info = ct->ct_addr;
353 break;
354 case CLGET_FD:
355 *(int *)info = ct->ct_sock;
356 break;
357 case CLGET_XID:
358 /*
359 * use the knowledge that xid is the
360 * first element in the call structure *.
361 * This will get the xid of the PREVIOUS call
362 */
fb1ae1ee
JM
363 memcpy (&ui32, ct->ct_mcall, sizeof (ui32));
364 ul = ntohl (ui32);
4efbd5cb 365 memcpy (info, &ul, sizeof (ul));
e852e889
UD
366 break;
367 case CLSET_XID:
368 /* This will set the xid of the NEXT call */
fb1ae1ee
JM
369 memcpy (&ul, info, sizeof (ul));
370 ui32 = htonl (ul - 1);
371 memcpy (ct->ct_mcall, &ui32, sizeof (ui32));
e852e889 372 /* decrement by 1 as clntunix_call() increments once */
f0ccf6ea 373 break;
e852e889
UD
374 case CLGET_VERS:
375 /*
376 * This RELIES on the information that, in the call body,
377 * the version number field is the fifth field from the
6f65e668 378 * beginning of the RPC header. MUST be changed if the
e852e889
UD
379 * call_struct is changed
380 */
fb1ae1ee
JM
381 memcpy (&ui32, ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT, sizeof (ui32));
382 ul = ntohl (ui32);
383 memcpy (info, &ul, sizeof (ul));
e852e889
UD
384 break;
385 case CLSET_VERS:
fb1ae1ee
JM
386 memcpy (&ul, info, sizeof (ul));
387 ui32 = htonl (ul);
388 memcpy (ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT, &ui32, sizeof (ui32));
e852e889
UD
389 break;
390 case CLGET_PROG:
391 /*
392 * This RELIES on the information that, in the call body,
393 * the program number field is the field from the
6f65e668 394 * beginning of the RPC header. MUST be changed if the
e852e889
UD
395 * call_struct is changed
396 */
fb1ae1ee
JM
397 memcpy (&ui32, ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT, sizeof (ui32));
398 ul = ntohl (ui32);
399 memcpy (info, &ul, sizeof (ul));
e852e889
UD
400 break;
401 case CLSET_PROG:
fb1ae1ee
JM
402 memcpy (&ul, info, sizeof (ul));
403 ui32 = htonl (ul);
404 memcpy (ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT, &ui32, sizeof (ui32));
e852e889
UD
405 break;
406 /* The following are only possible with TI-RPC */
407 case CLGET_RETRY_TIMEOUT:
408 case CLSET_RETRY_TIMEOUT:
409 case CLGET_SVC_ADDR:
410 case CLSET_SVC_ADDR:
411 case CLSET_PUSH_TIMOD:
412 case CLSET_POP_TIMOD:
413 default:
414 return FALSE;
415 }
416 return TRUE;
417}
418
419
420static void
421clntunix_destroy (CLIENT *h)
422{
423 struct ct_data *ct =
424 (struct ct_data *) h->cl_private;
425
426 if (ct->ct_closeit)
427 {
c0b50509 428 (void) __close (ct->ct_sock);
e852e889
UD
429 }
430 XDR_DESTROY (&(ct->ct_xdrs));
431 mem_free ((caddr_t) ct, sizeof (struct ct_data));
432 mem_free ((caddr_t) h, sizeof (CLIENT));
433}
434
e852e889 435static int
d1275afe 436__msgread (int sock, void *data, size_t cnt)
e852e889 437{
d1275afe 438 struct iovec iov;
e852e889 439 struct msghdr msg;
c5720a93 440#ifdef SCM_CREDENTIALS
d1275afe 441 static char cm[CMSG_SPACE(sizeof (struct ucred))];
c5720a93 442#endif
d1275afe 443 int len;
e852e889 444
d1275afe
AS
445 iov.iov_base = data;
446 iov.iov_len = cnt;
e852e889 447
d1275afe 448 msg.msg_iov = &iov;
e852e889
UD
449 msg.msg_iovlen = 1;
450 msg.msg_name = NULL;
451 msg.msg_namelen = 0;
c5720a93 452#ifdef SCM_CREDENTIALS
d1275afe
AS
453 msg.msg_control = (caddr_t) &cm;
454 msg.msg_controllen = CMSG_SPACE(sizeof (struct ucred));
c5720a93 455#endif
e852e889
UD
456 msg.msg_flags = 0;
457
7cabd57c 458#ifdef SO_PASSCRED
d76240d7
RM
459 {
460 int on = 1;
b2bffca2 461 if (__setsockopt (sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof (on)))
d76240d7
RM
462 return -1;
463 }
7cabd57c 464#endif
e852e889 465
d1275afe 466 restart:
b2bffca2 467 len = __recvmsg (sock, &msg, 0);
d1275afe
AS
468 if (len >= 0)
469 {
470 if (msg.msg_flags & MSG_CTRUNC || len == 0)
471 return 0;
472 else
473 return len;
474 }
475 if (errno == EINTR)
476 goto restart;
477 return -1;
e852e889
UD
478}
479
480static int
d1275afe 481__msgwrite (int sock, void *data, size_t cnt)
e852e889 482{
7ce241a0 483#ifndef SCM_CREDENTIALS
e852e889
UD
484 /* We cannot implement this reliably. */
485 __set_errno (ENOSYS);
786a5421 486 return -1;
e852e889 487#else
d1275afe 488 struct iovec iov;
e852e889 489 struct msghdr msg;
d1275afe
AS
490 struct cmsghdr *cmsg = alloca (CMSG_SPACE(sizeof (struct ucred)));
491 struct ucred cred;
492 int len;
e852e889 493
e852e889
UD
494 /* XXX I'm not sure, if gete?id() is always correct, or if we should use
495 get?id(). But since keyserv needs geteuid(), we have no other chance.
496 It would be much better, if the kernel could pass both to the server. */
d1275afe
AS
497 cred.pid = __getpid ();
498 cred.uid = __geteuid ();
499 cred.gid = __getegid ();
500
501 memcpy (CMSG_DATA(cmsg), &cred, sizeof (struct ucred));
502 cmsg->cmsg_level = SOL_SOCKET;
503 cmsg->cmsg_type = SCM_CREDENTIALS;
504 cmsg->cmsg_len = sizeof(*cmsg) + sizeof(struct ucred);
e852e889 505
d1275afe
AS
506 iov.iov_base = data;
507 iov.iov_len = cnt;
508
509 msg.msg_iov = &iov;
e852e889
UD
510 msg.msg_iovlen = 1;
511 msg.msg_name = NULL;
512 msg.msg_namelen = 0;
d1275afe
AS
513 msg.msg_control = cmsg;
514 msg.msg_controllen = CMSG_ALIGN(cmsg->cmsg_len);
e852e889
UD
515 msg.msg_flags = 0;
516
d1275afe 517 restart:
b2bffca2 518 len = __sendmsg (sock, &msg, 0);
d1275afe
AS
519 if (len >= 0)
520 return len;
521 if (errno == EINTR)
522 goto restart;
523 return -1;
524
e852e889
UD
525#endif
526}
527
528
529/*
530 * Interface between xdr serializer and unix connection.
531 * Behaves like the system calls, read & write, but keeps some error state
532 * around for the rpc level.
533 */
534static int
535readunix (char *ctptr, char *buf, int len)
536{
537 struct ct_data *ct = (struct ct_data *) ctptr;
538 struct pollfd fd;
539 int milliseconds = ((ct->ct_wait.tv_sec * 1000)
540 + (ct->ct_wait.tv_usec / 1000));
541
542 if (len == 0)
543 return 0;
544
545 fd.fd = ct->ct_sock;
546 fd.events = POLLIN;
547 while (TRUE)
548 {
549 switch (__poll (&fd, 1, milliseconds))
7b57bfe5
UD
550 {
551 case 0:
552 ct->ct_error.re_status = RPC_TIMEDOUT;
553 return -1;
554
555 case -1:
556 if (errno == EINTR)
557 continue;
558 ct->ct_error.re_status = RPC_CANTRECV;
559 ct->ct_error.re_errno = errno;
560 return -1;
561 }
e852e889
UD
562 break;
563 }
564 switch (len = __msgread (ct->ct_sock, buf, len))
565 {
566
567 case 0:
568 /* premature eof */
569 ct->ct_error.re_errno = ECONNRESET;
570 ct->ct_error.re_status = RPC_CANTRECV;
571 len = -1; /* it's really an error */
572 break;
573
574 case -1:
575 ct->ct_error.re_errno = errno;
576 ct->ct_error.re_status = RPC_CANTRECV;
577 break;
578 }
579 return len;
580}
581
582static int
583writeunix (char *ctptr, char *buf, int len)
584{
585 int i, cnt;
586 struct ct_data *ct = (struct ct_data *) ctptr;
587
588 for (cnt = len; cnt > 0; cnt -= i, buf += i)
589 {
590 if ((i = __msgwrite (ct->ct_sock, buf, cnt)) == -1)
591 {
592 ct->ct_error.re_errno = errno;
593 ct->ct_error.re_status = RPC_CANTSEND;
594 return -1;
595 }
596 }
597 return len;
598}