handle multiple uba's
[unix-history] / usr / src / sys / kern / uipc_socket.c
CommitLineData
4a2ddbf1 1/* uipc_socket.c 4.42 82/06/20 */
ce9d8eb4
BJ
2
3#include "../h/param.h"
92a533e6 4#include "../h/systm.h"
ce9d8eb4
BJ
5#include "../h/dir.h"
6#include "../h/user.h"
92a533e6
BJ
7#include "../h/proc.h"
8#include "../h/file.h"
ce9d8eb4 9#include "../h/inode.h"
92a533e6 10#include "../h/buf.h"
ce9d8eb4 11#include "../h/mbuf.h"
92a533e6
BJ
12#include "../h/protosw.h"
13#include "../h/socket.h"
14#include "../h/socketvar.h"
ae921915 15#include "../h/stat.h"
b8acc34d 16#include "../h/ioctl.h"
c5ff2e32
BJ
17#include "../net/in.h"
18#include "../net/in_systm.h"
2a4251c4 19#include "../net/route.h"
ce9d8eb4 20
ce9d8eb4 21/*
cc15ab5d
BJ
22 * Socket support routines.
23 *
24 * DEAL WITH INTERRUPT NOTIFICATION.
ce9d8eb4 25 */
ce9d8eb4
BJ
26
27/*
28 * Create a socket.
29 */
2b4b57cd 30socreate(aso, type, asp, asa, options)
ce9d8eb4
BJ
31 struct socket **aso;
32 int type;
2b4b57cd
BJ
33 struct sockproto *asp;
34 struct sockaddr *asa;
92a533e6 35 int options;
ce9d8eb4
BJ
36{
37 register struct protosw *prp;
38 register struct socket *so;
39 struct mbuf *m;
cc15ab5d 40 int pf, proto, error;
ce9d8eb4
BJ
41
42 /*
cc15ab5d
BJ
43 * Use process standard protocol/protocol family if none
44 * specified by address argument.
ce9d8eb4 45 */
2b4b57cd 46 if (asp == 0) {
cc15ab5d 47 pf = PF_INET; /* should be u.u_protof */
ce9d8eb4
BJ
48 proto = 0;
49 } else {
2b4b57cd
BJ
50 pf = asp->sp_family;
51 proto = asp->sp_protocol;
ce9d8eb4 52 }
cc15ab5d
BJ
53
54 /*
55 * If protocol specified, look for it, otherwise
56 * for a protocol of the correct type in the right family.
57 */
58 if (proto)
59 prp = pffindproto(pf, proto);
60 else
61 prp = pffindtype(pf, type);
62 if (prp == 0)
63 return (EPROTONOSUPPORT);
ce9d8eb4
BJ
64
65 /*
66 * Get a socket structure.
67 */
cc15ab5d 68 m = m_getclr(M_WAIT);
ce9d8eb4
BJ
69 if (m == 0)
70 return (ENOBUFS);
ce9d8eb4 71 so = mtod(m, struct socket *);
92a533e6 72 so->so_options = options;
62364f0e
BJ
73 so->so_state = 0;
74 if (u.u_uid == 0)
75 so->so_state = SS_PRIV;
ce9d8eb4
BJ
76
77 /*
cc15ab5d
BJ
78 * Attach protocol to socket, initializing
79 * and reserving resources.
ce9d8eb4
BJ
80 */
81 so->so_proto = prp;
b91acce4
BJ
82 error = (*prp->pr_usrreq)(so, PRU_ATTACH, 0, asa);
83 if (error) {
de48daf3
BJ
84 if (so->so_snd.sb_mbmax || so->so_rcv.sb_mbmax)
85 panic("socreate");
86 so->so_state |= SS_USERGONE;
87 sofree(so);
cc15ab5d 88 return (error);
ce9d8eb4
BJ
89 }
90 *aso = so;
91 return (0);
92}
93
ae921915
BJ
94sofree(so)
95 struct socket *so;
96{
97
4ad99bae
BJ
98 if (so->so_pcb || (so->so_state & SS_USERGONE) == 0)
99 return;
100 sbrelease(&so->so_snd);
101 sbrelease(&so->so_rcv);
2752c877 102 (void) m_free(dtom(so));
ae921915
BJ
103}
104
92a533e6 105/*
cc15ab5d
BJ
106 * Close a socket on last file table reference removal.
107 * Initiate disconnect if connected.
108 * Free socket when disconnect complete.
89900a09
BJ
109 *
110 * THIS IS REALLY A UNIX INTERFACE ROUTINE
92a533e6 111 */
89900a09 112soclose(so, exiting)
92a533e6 113 register struct socket *so;
89900a09 114 int exiting;
92a533e6 115{
cc15ab5d
BJ
116 int s = splnet(); /* conservative */
117
118 if (so->so_pcb == 0)
119 goto discard;
1ee9e088
BJ
120 if (exiting)
121 so->so_options |= SO_KEEPALIVE;
cc15ab5d
BJ
122 if (so->so_state & SS_ISCONNECTED) {
123 if ((so->so_state & SS_ISDISCONNECTING) == 0) {
2b4b57cd 124 u.u_error = sodisconnect(so, (struct sockaddr *)0);
cc15ab5d 125 if (u.u_error) {
89900a09
BJ
126 if (exiting)
127 goto drop;
cc15ab5d
BJ
128 splx(s);
129 return;
130 }
131 }
30b84e26 132 if ((so->so_options & SO_DONTLINGER) == 0) {
b8acc34d 133 if ((so->so_state & SS_ISDISCONNECTING) &&
62364f0e 134 (so->so_state & SS_NBIO) &&
89900a09 135 exiting == 0) {
b8acc34d
BJ
136 u.u_error = EINPROGRESS;
137 splx(s);
138 return;
139 }
89900a09 140 /* should use tsleep here, for at most linger */
b8acc34d
BJ
141 while (so->so_state & SS_ISCONNECTED)
142 sleep((caddr_t)&so->so_timeo, PZERO+1);
72857acf 143 }
cc15ab5d 144 }
89900a09 145drop:
37c0974a
SL
146 if (so->so_pcb) {
147 u.u_error = (*so->so_proto->pr_usrreq)(so, PRU_DETACH, 0, 0);
148 if (exiting == 0 && u.u_error) {
149 splx(s);
150 return;
151 }
152 }
cc15ab5d 153discard:
4ad99bae
BJ
154 so->so_state |= SS_USERGONE;
155 sofree(so);
cc15ab5d 156 splx(s);
92a533e6
BJ
157}
158
ae921915 159/*ARGSUSED*/
cc15ab5d 160sostat(so, sb)
92a533e6 161 struct socket *so;
cc15ab5d 162 struct stat *sb;
92a533e6
BJ
163{
164
5e35cca3
BJ
165 bzero((caddr_t)sb, sizeof (*sb)); /* XXX */
166 return (0); /* XXX */
92a533e6
BJ
167}
168
2b4b57cd
BJ
169/*
170 * Accept connection on a socket.
171 */
172soaccept(so, asa)
173 struct socket *so;
174 struct sockaddr *asa;
175{
176 int s = splnet();
177 int error;
178
2b4b57cd
BJ
179 if ((so->so_options & SO_ACCEPTCONN) == 0) {
180 error = EINVAL; /* XXX */
181 goto bad;
182 }
92d06f69
BJ
183 if ((so->so_state & SS_CONNAWAITING) == 0) {
184 error = ENOTCONN;
185 goto bad;
186 }
187 so->so_state &= ~SS_CONNAWAITING;
2b4b57cd
BJ
188 error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT, 0, (caddr_t)asa);
189bad:
190 splx(s);
191 return (error);
192}
193
ce9d8eb4 194/*
cc15ab5d
BJ
195 * Connect socket to a specified address.
196 * If already connected or connecting, then avoid
197 * the protocol entry, to keep its job simpler.
ce9d8eb4 198 */
2b4b57cd 199soconnect(so, asa)
ce9d8eb4 200 struct socket *so;
2b4b57cd 201 struct sockaddr *asa;
ce9d8eb4 202{
cc15ab5d
BJ
203 int s = splnet();
204 int error;
ce9d8eb4 205
cc15ab5d
BJ
206 if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) {
207 error = EISCONN;
208 goto bad;
209 }
2b4b57cd 210 error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT, 0, (caddr_t)asa);
cc15ab5d
BJ
211bad:
212 splx(s);
213 return (error);
ce9d8eb4
BJ
214}
215
216/*
cc15ab5d
BJ
217 * Disconnect from a socket.
218 * Address parameter is from system call for later multicast
219 * protocols. Check to make sure that connected and no disconnect
220 * in progress (for protocol's sake), and then invoke protocol.
ce9d8eb4 221 */
2b4b57cd 222sodisconnect(so, asa)
ce9d8eb4 223 struct socket *so;
2b4b57cd 224 struct sockaddr *asa;
ce9d8eb4 225{
cc15ab5d
BJ
226 int s = splnet();
227 int error;
ce9d8eb4 228
cc15ab5d
BJ
229 if ((so->so_state & SS_ISCONNECTED) == 0) {
230 error = ENOTCONN;
231 goto bad;
ce9d8eb4 232 }
cc15ab5d
BJ
233 if (so->so_state & SS_ISDISCONNECTING) {
234 error = EALREADY;
235 goto bad;
ce9d8eb4 236 }
2b4b57cd 237 error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, 0, asa);
cc15ab5d
BJ
238bad:
239 splx(s);
240 return (error);
ce9d8eb4
BJ
241}
242
cc15ab5d
BJ
243/*
244 * Send on a socket.
245 * If send must go all at once and message is larger than
246 * send buffering, then hard error.
247 * Lock against other senders.
248 * If must go all at once and not enough room now, then
249 * inform user that this would block and do nothing.
250 */
2b4b57cd 251sosend(so, asa)
ce9d8eb4 252 register struct socket *so;
2b4b57cd 253 struct sockaddr *asa;
ce9d8eb4 254{
cc15ab5d
BJ
255 struct mbuf *top = 0;
256 register struct mbuf *m, **mp = ⊤
ae921915
BJ
257 register u_int len;
258 int error = 0, space, s;
ce9d8eb4 259
cc15ab5d
BJ
260 if (sosendallatonce(so) && u.u_count > so->so_snd.sb_hiwat)
261 return (EMSGSIZE);
0f90f987
BJ
262#ifdef notdef
263 /* NEED TO PREVENT BUSY WAITING IN SELECT FOR WRITING */
62364f0e 264 if ((so->so_snd.sb_flags & SB_LOCK) && (so->so_state & SS_NBIO))
cc15ab5d 265 return (EWOULDBLOCK);
0f90f987
BJ
266#endif
267restart:
cc15ab5d
BJ
268 sblock(&so->so_snd);
269#define snderr(errno) { error = errno; splx(s); goto release; }
270
cc15ab5d 271again:
0f90f987
BJ
272 s = splnet();
273 if (so->so_state & SS_CANTSENDMORE) {
274 psignal(u.u_procp, SIGPIPE);
275 snderr(EPIPE);
276 }
4c078bb2
BJ
277 if (so->so_error) {
278 error = so->so_error;
0f90f987 279 so->so_error = 0; /* ??? */
4c078bb2
BJ
280 splx(s);
281 goto release;
282 }
cc15ab5d
BJ
283 if ((so->so_state & SS_ISCONNECTED) == 0) {
284 if (so->so_proto->pr_flags & PR_CONNREQUIRED)
285 snderr(ENOTCONN);
2b4b57cd 286 if (asa == 0)
cc15ab5d
BJ
287 snderr(EDESTADDRREQ);
288 }
cc15ab5d 289 if (top) {
2b4b57cd 290 error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, top, asa);
0f90f987 291 top = 0;
cc15ab5d
BJ
292 if (error) {
293 splx(s);
ce9d8eb4
BJ
294 goto release;
295 }
cc15ab5d 296 mp = ⊤
ce9d8eb4 297 }
b91acce4
BJ
298 if (u.u_count == 0) {
299 splx(s);
300 goto release;
301 }
50fc8df6 302 space = sbspace(&so->so_snd);
6fa47729 303 if (space <= 0 || sosendallatonce(so) && space < u.u_count) {
62364f0e 304 if (so->so_state & SS_NBIO)
cc15ab5d
BJ
305 snderr(EWOULDBLOCK);
306 sbunlock(&so->so_snd);
307 sbwait(&so->so_snd);
308 splx(s);
0f90f987 309 goto restart;
ce9d8eb4 310 }
cc15ab5d 311 splx(s);
50fc8df6 312 while (u.u_count && space > 0) {
cc15ab5d
BJ
313 MGET(m, 1);
314 if (m == NULL) {
0f90f987 315 error = ENOBUFS; /* SIGPIPE? */
cc15ab5d 316 goto release;
ce9d8eb4 317 }
c5ff2e32 318 if (u.u_count >= CLBYTES && space >= CLBYTES) {
cc15ab5d 319 register struct mbuf *p;
c5ff2e32 320 MCLGET(p, 1);
cc15ab5d
BJ
321 if (p == 0)
322 goto nopages;
323 m->m_off = (int)p - (int)m;
c5ff2e32 324 len = CLBYTES;
cc15ab5d 325 } else {
ce9d8eb4 326nopages:
cc15ab5d
BJ
327 m->m_off = MMINOFF;
328 len = MIN(MLEN, u.u_count);
ce9d8eb4 329 }
cc15ab5d
BJ
330 iomove(mtod(m, caddr_t), len, B_WRITE);
331 m->m_len = len;
332 *mp = m;
333 mp = &m->m_next;
50fc8df6 334 space = sbspace(&so->so_snd);
ce9d8eb4 335 }
cc15ab5d
BJ
336 goto again;
337
ce9d8eb4 338release:
cc15ab5d 339 sbunlock(&so->so_snd);
0f90f987
BJ
340 if (top)
341 m_freem(top);
ce9d8eb4
BJ
342 return (error);
343}
344
2b4b57cd 345soreceive(so, asa)
ce9d8eb4 346 register struct socket *so;
2b4b57cd 347 struct sockaddr *asa;
ce9d8eb4
BJ
348{
349 register struct mbuf *m, *n;
ae921915 350 u_int len;
edebca28
BJ
351 int eor, s, error = 0, cnt = u.u_count;
352 caddr_t base = u.u_base;
ce9d8eb4 353
cc15ab5d
BJ
354restart:
355 sblock(&so->so_rcv);
356 s = splnet();
357
358#define rcverr(errno) { error = errno; splx(s); goto release; }
ce9d8eb4 359 if (so->so_rcv.sb_cc == 0) {
4c078bb2
BJ
360 if (so->so_error) {
361 error = so->so_error;
362 so->so_error = 0;
363 splx(s);
364 goto release;
365 }
cc15ab5d
BJ
366 if (so->so_state & SS_CANTRCVMORE) {
367 splx(s);
368 goto release;
369 }
196d84fd
BJ
370 if ((so->so_state & SS_ISCONNECTED) == 0 &&
371 (so->so_proto->pr_flags & PR_CONNREQUIRED))
372 rcverr(ENOTCONN);
62364f0e 373 if (so->so_state & SS_NBIO)
4c078bb2 374 rcverr(EWOULDBLOCK);
cc15ab5d 375 sbunlock(&so->so_rcv);
2752c877 376 sbwait(&so->so_rcv);
a4f6d93d 377 splx(s);
cc15ab5d 378 goto restart;
ce9d8eb4 379 }
92a533e6 380 m = so->so_rcv.sb_mb;
ce9d8eb4
BJ
381 if (m == 0)
382 panic("receive");
b031e888 383 if (so->so_proto->pr_flags & PR_ADDR) {
b031e888
BJ
384 if (m->m_len != sizeof (struct sockaddr))
385 panic("soreceive addr");
386 if (asa)
387 bcopy(mtod(m, caddr_t), (caddr_t)asa, sizeof (*asa));
b031e888
BJ
388 so->so_rcv.sb_cc -= m->m_len;
389 so->so_rcv.sb_mbcnt -= MSIZE;
50fc8df6 390 m = m_free(m);
cc15ab5d
BJ
391 if (m == 0)
392 panic("receive 2");
50fc8df6 393 so->so_rcv.sb_mb = m;
cc15ab5d 394 }
edebca28
BJ
395 so->so_state &= ~SS_RCVATMARK;
396 if (so->so_oobmark && cnt > so->so_oobmark)
397 cnt = so->so_oobmark;
ce9d8eb4
BJ
398 eor = 0;
399 do {
edebca28 400 len = MIN(m->m_len, cnt);
ce9d8eb4
BJ
401 splx(s);
402 iomove(mtod(m, caddr_t), len, B_READ);
edebca28 403 cnt -= len;
ce9d8eb4
BJ
404 s = splnet();
405 if (len == m->m_len) {
e495e1cc
BJ
406 eor = (int)m->m_act;
407 sbfree(&so->so_rcv, m);
408 so->so_rcv.sb_mb = m->m_next;
ce9d8eb4
BJ
409 MFREE(m, n);
410 } else {
411 m->m_off += len;
412 m->m_len -= len;
92a533e6 413 so->so_rcv.sb_cc -= len;
ce9d8eb4 414 }
edebca28 415 } while ((m = so->so_rcv.sb_mb) && cnt && !eor);
ce9d8eb4
BJ
416 if ((so->so_proto->pr_flags & PR_ATOMIC) && eor == 0)
417 do {
ce9d8eb4 418 if (m == 0)
cc15ab5d
BJ
419 panic("receive 3");
420 sbfree(&so->so_rcv, m);
ce9d8eb4
BJ
421 eor = (int)m->m_act;
422 so->so_rcv.sb_mb = m->m_next;
423 MFREE(m, n);
cc15ab5d 424 m = n;
ce9d8eb4 425 } while (eor == 0);
cc15ab5d
BJ
426 if ((so->so_proto->pr_flags & PR_WANTRCVD) && so->so_pcb)
427 (*so->so_proto->pr_usrreq)(so, PRU_RCVD, 0, 0);
edebca28
BJ
428 if (so->so_oobmark) {
429 so->so_oobmark -= u.u_base - base;
430 if (so->so_oobmark == 0)
431 so->so_state |= SS_RCVATMARK;
432 }
cc15ab5d 433release:
ae921915 434 sbunlock(&so->so_rcv);
cc15ab5d 435 splx(s);
ae921915 436 return (error);
92a533e6
BJ
437}
438
edebca28
BJ
439sohasoutofband(so)
440 struct socket *so;
441{
442
443 if (so->so_pgrp == 0)
444 return;
445 if (so->so_pgrp > 0)
446 gsignal(so->so_pgrp, SIGURG);
0a0f7cbb
BJ
447 else {
448 struct proc *p = pfind(-so->so_pgrp);
449
450 if (p)
451 psignal(p, SIGURG);
452 }
edebca28
BJ
453}
454
ae921915
BJ
455/*ARGSUSED*/
456soioctl(so, cmd, cmdp)
92a533e6
BJ
457 register struct socket *so;
458 int cmd;
459 register caddr_t cmdp;
460{
461
2156d53c 462 switch (cmd) {
92a533e6 463
30b84e26
BJ
464 case FIONBIO: {
465 int nbio;
466 if (copyin(cmdp, (caddr_t)&nbio, sizeof (nbio))) {
467 u.u_error = EFAULT;
468 return;
469 }
470 if (nbio)
62364f0e 471 so->so_state |= SS_NBIO;
30b84e26 472 else
62364f0e 473 so->so_state &= ~SS_NBIO;
30b84e26
BJ
474 return;
475 }
476
477 case FIOASYNC: {
478 int async;
479 if (copyin(cmdp, (caddr_t)&async, sizeof (async))) {
480 u.u_error = EFAULT;
481 return;
482 }
483 if (async)
62364f0e 484 so->so_state |= SS_ASYNC;
30b84e26 485 else
62364f0e 486 so->so_state &= ~SS_ASYNC;
30b84e26
BJ
487 return;
488 }
489
490 case SIOCSKEEP: {
491 int keep;
492 if (copyin(cmdp, (caddr_t)&keep, sizeof (keep))) {
493 u.u_error = EFAULT;
494 return;
495 }
496 if (keep)
62364f0e 497 so->so_options &= ~SO_KEEPALIVE;
30b84e26 498 else
62364f0e 499 so->so_options |= SO_KEEPALIVE;
30b84e26
BJ
500 return;
501 }
502
503 case SIOCGKEEP: {
62364f0e 504 int keep = (so->so_options & SO_KEEPALIVE) != 0;
30b84e26
BJ
505 if (copyout((caddr_t)&keep, cmdp, sizeof (keep)))
506 u.u_error = EFAULT;
507 return;
508 }
509
510 case SIOCSLINGER: {
511 int linger;
512 if (copyin(cmdp, (caddr_t)&linger, sizeof (linger))) {
513 u.u_error = EFAULT;
514 return;
515 }
516 so->so_linger = linger;
517 if (so->so_linger)
518 so->so_options &= ~SO_DONTLINGER;
519 else
520 so->so_options |= SO_DONTLINGER;
521 return;
522 }
523
524 case SIOCGLINGER: {
525 int linger = so->so_linger;
526 if (copyout((caddr_t)&linger, cmdp, sizeof (linger))) {
527 u.u_error = EFAULT;
528 return;
529 }
530 }
edebca28
BJ
531 case SIOCSPGRP: {
532 int pgrp;
533 if (copyin(cmdp, (caddr_t)&pgrp, sizeof (pgrp))) {
534 u.u_error = EFAULT;
535 return;
536 }
537 so->so_pgrp = pgrp;
538 return;
539 }
540
541 case SIOCGPGRP: {
542 int pgrp = so->so_pgrp;
543 if (copyout((caddr_t)&pgrp, cmdp, sizeof (pgrp))) {
544 u.u_error = EFAULT;
545 return;
546 }
547 }
30b84e26 548
b8acc34d
BJ
549 case SIOCDONE: {
550 int flags;
551 if (copyin(cmdp, (caddr_t)&flags, sizeof (flags))) {
552 u.u_error = EFAULT;
553 return;
554 }
30b84e26 555 flags++;
b8acc34d
BJ
556 if (flags & FREAD) {
557 int s = splimp();
558 socantrcvmore(so);
559 sbflush(&so->so_rcv);
785e10b7 560 splx(s);
b8acc34d
BJ
561 }
562 if (flags & FWRITE)
c30cd845 563 u.u_error = (*so->so_proto->pr_usrreq)(so, PRU_SHUTDOWN, (struct mbuf *)0, 0);
b8acc34d
BJ
564 return;
565 }
566
edebca28
BJ
567 case SIOCSENDOOB: {
568 char oob;
569 struct mbuf *m;
570 if (copyin(cmdp, (caddr_t)&oob, sizeof (oob))) {
571 u.u_error = EFAULT;
572 return;
573 }
574 m = m_get(M_DONTWAIT);
575 if (m == 0) {
576 u.u_error = ENOBUFS;
577 return;
578 }
579 m->m_off = MMINOFF;
580 m->m_len = 1;
581 *mtod(m, caddr_t) = oob;
582 (*so->so_proto->pr_usrreq)(so, PRU_SENDOOB, m, 0);
583 return;
584 }
585
586 case SIOCRCVOOB: {
587 struct mbuf *m = m_get(M_DONTWAIT);
588 if (m == 0) {
589 u.u_error = ENOBUFS;
590 return;
591 }
592 m->m_off = MMINOFF; *mtod(m, caddr_t) = 0;
593 (*so->so_proto->pr_usrreq)(so, PRU_RCVOOB, m, 0);
594 if (copyout(mtod(m, caddr_t), cmdp, sizeof (char))) {
595 u.u_error = EFAULT;
596 return;
597 }
598 m_free(m);
599 return;
600 }
601
602 case SIOCATMARK: {
603 int atmark = (so->so_state&SS_RCVATMARK) != 0;
604 if (copyout((caddr_t)&atmark, cmdp, sizeof (atmark))) {
605 u.u_error = EFAULT;
606 return;
607 }
608 return;
609 }
2a4251c4
SL
610
611 /* routing table update calls */
612 case SIOCADDRT:
613 case SIOCDELRT:
614 case SIOCCHGRT: {
615 struct rtentry route;
2a4251c4
SL
616 if (!suser())
617 return;
2a4251c4
SL
618 if (copyin(cmdp, (caddr_t)&route, sizeof (route))) {
619 u.u_error = EFAULT;
620 return;
621 }
622 u.u_error = rtrequest(cmd, &route);
623 return;
624 }
625
5e1eeba0 626 /* type/protocol specific ioctls */
92a533e6 627 }
5e1eeba0 628 u.u_error = EOPNOTSUPP;
ce9d8eb4 629}