spiped 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/ext/spiped/extconf.rb +3 -0
  3. data/ext/spiped/spiped-source/BUILDING +46 -0
  4. data/ext/spiped/spiped-source/CHANGELOG +44 -0
  5. data/ext/spiped/spiped-source/COPYRIGHT +33 -0
  6. data/ext/spiped/spiped-source/Makefile +47 -0
  7. data/ext/spiped/spiped-source/Makefile.POSIX +27 -0
  8. data/ext/spiped/spiped-source/Makefile.inc +20 -0
  9. data/ext/spiped/spiped-source/Makefile.prog +23 -0
  10. data/ext/spiped/spiped-source/POSIX/README +10 -0
  11. data/ext/spiped/spiped-source/POSIX/posix-cflags.sh +10 -0
  12. data/ext/spiped/spiped-source/POSIX/posix-clock_realtime.c +3 -0
  13. data/ext/spiped/spiped-source/POSIX/posix-l.c +1 -0
  14. data/ext/spiped/spiped-source/POSIX/posix-l.sh +14 -0
  15. data/ext/spiped/spiped-source/POSIX/posix-msg_nosignal.c +3 -0
  16. data/ext/spiped/spiped-source/README +198 -0
  17. data/ext/spiped/spiped-source/STYLE +151 -0
  18. data/ext/spiped/spiped-source/lib/dnsthread/dnsthread.c +464 -0
  19. data/ext/spiped/spiped-source/lib/dnsthread/dnsthread.h +45 -0
  20. data/ext/spiped/spiped-source/libcperciva/alg/sha256.c +442 -0
  21. data/ext/spiped/spiped-source/libcperciva/alg/sha256.h +95 -0
  22. data/ext/spiped/spiped-source/libcperciva/cpusupport/Build/cpusupport-X86-AESNI.c +13 -0
  23. data/ext/spiped/spiped-source/libcperciva/cpusupport/Build/cpusupport-X86-CPUID.c +8 -0
  24. data/ext/spiped/spiped-source/libcperciva/cpusupport/Build/cpusupport.sh +37 -0
  25. data/ext/spiped/spiped-source/libcperciva/cpusupport/cpusupport.h +63 -0
  26. data/ext/spiped/spiped-source/libcperciva/cpusupport/cpusupport_x86_aesni.c +30 -0
  27. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_aes.c +166 -0
  28. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_aes.h +31 -0
  29. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_aes_aesni.c +229 -0
  30. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_aes_aesni.h +31 -0
  31. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_aesctr.c +124 -0
  32. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_aesctr.h +41 -0
  33. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_dh.c +293 -0
  34. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_dh.h +43 -0
  35. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_dh_group14.c +46 -0
  36. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_dh_group14.h +9 -0
  37. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_entropy.c +215 -0
  38. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_entropy.h +14 -0
  39. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_verify_bytes.c +21 -0
  40. data/ext/spiped/spiped-source/libcperciva/crypto/crypto_verify_bytes.h +14 -0
  41. data/ext/spiped/spiped-source/libcperciva/datastruct/elasticarray.c +276 -0
  42. data/ext/spiped/spiped-source/libcperciva/datastruct/elasticarray.h +167 -0
  43. data/ext/spiped/spiped-source/libcperciva/datastruct/mpool.h +85 -0
  44. data/ext/spiped/spiped-source/libcperciva/datastruct/ptrheap.c +334 -0
  45. data/ext/spiped/spiped-source/libcperciva/datastruct/ptrheap.h +89 -0
  46. data/ext/spiped/spiped-source/libcperciva/datastruct/timerqueue.c +241 -0
  47. data/ext/spiped/spiped-source/libcperciva/datastruct/timerqueue.h +60 -0
  48. data/ext/spiped/spiped-source/libcperciva/events/events.c +203 -0
  49. data/ext/spiped/spiped-source/libcperciva/events/events.h +106 -0
  50. data/ext/spiped/spiped-source/libcperciva/events/events_immediate.c +149 -0
  51. data/ext/spiped/spiped-source/libcperciva/events/events_internal.h +95 -0
  52. data/ext/spiped/spiped-source/libcperciva/events/events_network.c +347 -0
  53. data/ext/spiped/spiped-source/libcperciva/events/events_network_selectstats.c +106 -0
  54. data/ext/spiped/spiped-source/libcperciva/events/events_timer.c +273 -0
  55. data/ext/spiped/spiped-source/libcperciva/network/network.h +95 -0
  56. data/ext/spiped/spiped-source/libcperciva/network/network_accept.c +103 -0
  57. data/ext/spiped/spiped-source/libcperciva/network/network_connect.c +258 -0
  58. data/ext/spiped/spiped-source/libcperciva/network/network_read.c +155 -0
  59. data/ext/spiped/spiped-source/libcperciva/network/network_write.c +188 -0
  60. data/ext/spiped/spiped-source/libcperciva/util/asprintf.c +49 -0
  61. data/ext/spiped/spiped-source/libcperciva/util/asprintf.h +16 -0
  62. data/ext/spiped/spiped-source/libcperciva/util/daemonize.c +134 -0
  63. data/ext/spiped/spiped-source/libcperciva/util/daemonize.h +10 -0
  64. data/ext/spiped/spiped-source/libcperciva/util/entropy.c +76 -0
  65. data/ext/spiped/spiped-source/libcperciva/util/entropy.h +13 -0
  66. data/ext/spiped/spiped-source/libcperciva/util/imalloc.h +33 -0
  67. data/ext/spiped/spiped-source/libcperciva/util/insecure_memzero.c +19 -0
  68. data/ext/spiped/spiped-source/libcperciva/util/insecure_memzero.h +33 -0
  69. data/ext/spiped/spiped-source/libcperciva/util/monoclock.c +52 -0
  70. data/ext/spiped/spiped-source/libcperciva/util/monoclock.h +14 -0
  71. data/ext/spiped/spiped-source/libcperciva/util/noeintr.c +54 -0
  72. data/ext/spiped/spiped-source/libcperciva/util/noeintr.h +14 -0
  73. data/ext/spiped/spiped-source/libcperciva/util/sock.c +472 -0
  74. data/ext/spiped/spiped-source/libcperciva/util/sock.h +56 -0
  75. data/ext/spiped/spiped-source/libcperciva/util/sock_internal.h +14 -0
  76. data/ext/spiped/spiped-source/libcperciva/util/sock_util.c +271 -0
  77. data/ext/spiped/spiped-source/libcperciva/util/sock_util.h +51 -0
  78. data/ext/spiped/spiped-source/libcperciva/util/sysendian.h +146 -0
  79. data/ext/spiped/spiped-source/libcperciva/util/warnp.c +76 -0
  80. data/ext/spiped/spiped-source/libcperciva/util/warnp.h +59 -0
  81. data/ext/spiped/spiped-source/proto/proto_conn.c +362 -0
  82. data/ext/spiped/spiped-source/proto/proto_conn.h +25 -0
  83. data/ext/spiped/spiped-source/proto/proto_crypt.c +396 -0
  84. data/ext/spiped/spiped-source/proto/proto_crypt.h +102 -0
  85. data/ext/spiped/spiped-source/proto/proto_handshake.c +330 -0
  86. data/ext/spiped/spiped-source/proto/proto_handshake.h +30 -0
  87. data/ext/spiped/spiped-source/proto/proto_pipe.c +202 -0
  88. data/ext/spiped/spiped-source/proto/proto_pipe.h +23 -0
  89. data/ext/spiped/spiped-source/spipe/Makefile +90 -0
  90. data/ext/spiped/spiped-source/spipe/README +24 -0
  91. data/ext/spiped/spiped-source/spipe/main.c +178 -0
  92. data/ext/spiped/spiped-source/spipe/pushbits.c +101 -0
  93. data/ext/spiped/spiped-source/spipe/pushbits.h +10 -0
  94. data/ext/spiped/spiped-source/spipe/spipe.1 +60 -0
  95. data/ext/spiped/spiped-source/spiped/Makefile +98 -0
  96. data/ext/spiped/spiped-source/spiped/README +62 -0
  97. data/ext/spiped/spiped-source/spiped/dispatch.c +214 -0
  98. data/ext/spiped/spiped-source/spiped/dispatch.h +27 -0
  99. data/ext/spiped/spiped-source/spiped/main.c +267 -0
  100. data/ext/spiped/spiped-source/spiped/spiped.1 +112 -0
  101. data/lib/spiped.rb +3 -0
  102. metadata +143 -0
@@ -0,0 +1,188 @@
1
+ #include <sys/socket.h>
2
+
3
+ #include <assert.h>
4
+ #include <errno.h>
5
+ #include <limits.h>
6
+ #include <signal.h>
7
+ #include <stdint.h>
8
+ #include <stdlib.h>
9
+ #include <unistd.h>
10
+
11
+ #include "events.h"
12
+ #include "warnp.h"
13
+
14
+ #include "network.h"
15
+
16
+ /**
17
+ * POSIX.1-2008 requires that MSG_NOSIGNAL be defined as a flag for send(2)
18
+ * which has the effect of preventing SIGPIPE from being raised when writing
19
+ * to a descriptor which has been shut down. Unfortunately there are some
20
+ * platforms which are not POSIX.1-2008 compliant; we provide a workaround
21
+ * (-DPOSIXFAIL_MSG_NOSIGNAL) which instead blocks the SIGPIPE signal on such
22
+ * platforms.
23
+ *
24
+ * (This workaround could be used automatically, but requiring that it be
25
+ * explicitly enabled helps to get platforms fixed.)
26
+ */
27
+ #ifdef POSIXFAIL_MSG_NOSIGNAL
28
+ #ifndef MSG_NOSIGNAL
29
+ #define MSG_NOSIGNAL 0
30
+ #endif
31
+ #endif
32
+
33
+ struct network_write_cookie {
34
+ int (*callback)(void *, ssize_t);
35
+ void * cookie;
36
+ int fd;
37
+ const uint8_t * buf;
38
+ size_t buflen;
39
+ size_t minlen;
40
+ size_t bufpos;
41
+ };
42
+
43
+ /* Invoke the callback, clean up, and return the callback's status. */
44
+ static int
45
+ docallback(struct network_write_cookie * C, ssize_t nbytes)
46
+ {
47
+ int rc;
48
+
49
+ /* Invoke the callback. */
50
+ rc = (C->callback)(C->cookie, nbytes);
51
+
52
+ /* Clean up. */
53
+ free(C);
54
+
55
+ /* Return the callback's status. */
56
+ return (rc);
57
+ }
58
+
59
+ /* The socket is ready for reading/writing. */
60
+ static int
61
+ callback_buf(void * cookie)
62
+ {
63
+ struct network_write_cookie * C = cookie;
64
+ size_t oplen;
65
+ ssize_t len;
66
+ #ifdef POSIXFAIL_MSG_NOSIGNAL
67
+ void (*oldsig)(int);
68
+ #endif
69
+
70
+ /* If we don't have MSG_NOSIGNAL, catch SIGPIPE. */
71
+ #ifdef POSIXFAIL_MSG_NOSIGNAL
72
+ if ((oldsig = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) {
73
+ warnp("signal(SIGPIPE)");
74
+ goto failed;
75
+ }
76
+ #endif
77
+
78
+ /* Attempt to read/write data to/from the buffer. */
79
+ oplen = C->buflen - C->bufpos;
80
+ len = send(C->fd, C->buf + C->bufpos, oplen, MSG_NOSIGNAL);
81
+
82
+ /* We should never see a send length of zero. */
83
+ assert(len != 0);
84
+
85
+ /* If we set a SIGPIPE handler, restore the old one. */
86
+ #ifdef POSIXFAIL_MSG_NOSIGNAL
87
+ if (signal(SIGPIPE, oldsig) == SIG_ERR) {
88
+ warnp("signal(SIGPIPE)");
89
+ goto failed;
90
+ }
91
+ #endif
92
+
93
+ /* Failure? */
94
+ if (len == -1) {
95
+ /* Was it really an error, or just a try-again? */
96
+ if ((errno == EAGAIN) ||
97
+ (errno == EWOULDBLOCK) ||
98
+ (errno == EINTR))
99
+ goto tryagain;
100
+
101
+ /* Something went wrong. */
102
+ goto failed;
103
+ }
104
+
105
+ /* We processed some data. Do we need to keep going? */
106
+ if ((C->bufpos += len) < C->minlen)
107
+ goto tryagain;
108
+
109
+ /* Invoke the callback and return. */
110
+ return (docallback(C, C->bufpos));
111
+
112
+ tryagain:
113
+ /* Reset the event. */
114
+ if (events_network_register(callback_buf, C, C->fd,
115
+ EVENTS_NETWORK_OP_WRITE))
116
+ goto failed;
117
+
118
+ /* Callback was reset. */
119
+ return (0);
120
+
121
+ failed:
122
+ /* Invoke the callback with a failure status and return. */
123
+ return (docallback(C, -1));
124
+ }
125
+
126
+ /**
127
+ * network_write(fd, buf, buflen, minwrite, callback, cookie):
128
+ * Asynchronously write up to ${buflen} bytes of data from ${buf} to ${fd}.
129
+ * When at least ${minwrite} bytes have been written or on error, invoke
130
+ * ${callback}(${cookie}, lenwrit), where lenwrit is -1 on error and the
131
+ * number of bytes written (between ${minwrite} and ${buflen} inclusive)
132
+ * otherwise. Return a cookie which can be passed to network_write_cancel in
133
+ * order to cancel the write.
134
+ */
135
+ void *
136
+ network_write(int fd, const uint8_t * buf, size_t buflen, size_t minwrite,
137
+ int (* callback)(void *, ssize_t), void * cookie)
138
+ {
139
+ struct network_write_cookie * C;
140
+
141
+ /* Make sure buflen is non-zero. */
142
+ assert(buflen != 0);
143
+
144
+ /* Sanity-check: # bytes must fit into a ssize_t. */
145
+ assert(buflen <= SSIZE_MAX);
146
+
147
+ /* Bake a cookie. */
148
+ if ((C = malloc(sizeof(struct network_write_cookie))) == NULL)
149
+ goto err0;
150
+ C->callback = callback;
151
+ C->cookie = cookie;
152
+ C->fd = fd;
153
+ C->buf = buf;
154
+ C->buflen = buflen;
155
+ C->minlen = minwrite;
156
+ C->bufpos = 0;
157
+
158
+ /* Register a callback for network readiness. */
159
+ if (events_network_register(callback_buf, C, C->fd,
160
+ EVENTS_NETWORK_OP_WRITE))
161
+ goto err1;
162
+
163
+ /* Success! */
164
+ return (C);
165
+
166
+ err1:
167
+ free(C);
168
+ err0:
169
+ /* Failure! */
170
+ return (NULL);
171
+ }
172
+
173
+ /**
174
+ * network_write_cancel(cookie):
175
+ * Cancel the buffer write for which the cookie ${cookie} was returned by
176
+ * network_write. Do not invoke the callback associated with the write.
177
+ */
178
+ void
179
+ network_write_cancel(void * cookie)
180
+ {
181
+ struct network_write_cookie * C = cookie;
182
+
183
+ /* Kill the network event. */
184
+ events_network_cancel(C->fd, EVENTS_NETWORK_OP_WRITE);
185
+
186
+ /* Free the cookie. */
187
+ free(C);
188
+ }
@@ -0,0 +1,49 @@
1
+ #include <stdarg.h>
2
+ #include <stdio.h>
3
+ #include <stdlib.h>
4
+
5
+ #include "asprintf.h"
6
+
7
+ /**
8
+ * asprintf(ret, format, ...):
9
+ * Do asprintf(3) like GNU and BSD do.
10
+ */
11
+ int
12
+ asprintf(char ** ret, const char * format, ...)
13
+ {
14
+ va_list ap;
15
+ int len;
16
+ size_t buflen;
17
+
18
+ /* Figure out how long the string needs to be. */
19
+ va_start(ap, format);
20
+ len = vsnprintf(NULL, 0, format, ap);
21
+ va_end(ap);
22
+
23
+ /* Did we fail? */
24
+ if (len < 0)
25
+ goto err0;
26
+ buflen = (size_t)(len) + 1;
27
+
28
+ /* Allocate memory. */
29
+ if ((*ret = malloc(buflen)) == NULL)
30
+ goto err0;
31
+
32
+ /* Actually generate the string. */
33
+ va_start(ap, format);
34
+ len = vsnprintf(*ret, buflen, format, ap);
35
+ va_end(ap);
36
+
37
+ /* Did we fail? */
38
+ if (len < 0)
39
+ goto err1;
40
+
41
+ /* Success! */
42
+ return (len);
43
+
44
+ err1:
45
+ free(*ret);
46
+ err0:
47
+ /* Failure! */
48
+ return (-1);
49
+ }
@@ -0,0 +1,16 @@
1
+ #ifndef _ASPRINTF_H_
2
+ #define _ASPRINTF_H_
3
+
4
+ /* Avoid namespace collisions with BSD/GNU asprintf. */
5
+ #ifdef asprintf
6
+ #undef asprintf
7
+ #endif
8
+ #define asprintf libcperciva_asprintf
9
+
10
+ /**
11
+ * asprintf(ret, format, ...):
12
+ * Do asprintf(3) like GNU and BSD do.
13
+ */
14
+ int asprintf(char **, const char *, ...);
15
+
16
+ #endif /* !_ASPRINTF_H_ */
@@ -0,0 +1,134 @@
1
+ #include <errno.h>
2
+ #include <stdio.h>
3
+ #include <unistd.h>
4
+
5
+ #include "noeintr.h"
6
+ #include "warnp.h"
7
+
8
+ #include "daemonize.h"
9
+
10
+ /**
11
+ * daemonize(spid):
12
+ * Daemonize and write the process ID in decimal to a file named ${spid}.
13
+ * The parent process will exit only after the child has written its pid.
14
+ * On success, the child will return 0; on failure, the parent will return
15
+ * -1.
16
+ */
17
+ int
18
+ daemonize(const char * spid)
19
+ {
20
+ FILE * f;
21
+ int fd[2];
22
+ char dummy = 0;
23
+
24
+ /*
25
+ * Create a pipe for the child to notify the parent when it has
26
+ * finished daemonizing.
27
+ */
28
+ if (pipe(fd)) {
29
+ warnp("pipe");
30
+ goto err0;
31
+ }
32
+
33
+ /*
34
+ * Fork into the parent process (which waits for a poke and exits)
35
+ * and the child process (which keeps going).
36
+ */
37
+ switch (fork()) {
38
+ case -1:
39
+ /* Fork failed. */
40
+ warnp("fork");
41
+ goto err2;
42
+ case 0:
43
+ /* In child process. */
44
+ break;
45
+ default:
46
+ /*
47
+ * In parent process. Close write end of pipe so that if the
48
+ * client dies we will notice the pipe being reset.
49
+ */
50
+ while (close(fd[1])) {
51
+ if (errno == EINTR)
52
+ continue;
53
+ warnp("close");
54
+ goto err1;
55
+ }
56
+ do {
57
+ switch (read(fd[0], &dummy, 1)) {
58
+ case -1:
59
+ /* Error in read. */
60
+ break;
61
+ case 0:
62
+ /* EOF -- the child died without poking us. */
63
+ goto err1;
64
+ case 1:
65
+ /* We have been poked by the child. Exit. */
66
+ _exit(0);
67
+ }
68
+
69
+ /* Anything other than EINTR is bad. */
70
+ if (errno != EINTR) {
71
+ warnp("read");
72
+ goto err1;
73
+ }
74
+ } while (1);
75
+ }
76
+
77
+ /* Set ourselves to be a session leader. */
78
+ if (setsid() == -1) {
79
+ warnp("setsid");
80
+ goto die;
81
+ }
82
+
83
+ /* Write out our pid file. */
84
+ if ((f = fopen(spid, "w")) == NULL) {
85
+ warnp("fopen(%s)", spid);
86
+ goto die;
87
+ }
88
+ if (fprintf(f, "%d", getpid()) < 0) {
89
+ warnp("fprintf");
90
+ goto die;
91
+ }
92
+ if (fclose(f)) {
93
+ warnp("fclose");
94
+ goto die;
95
+ }
96
+
97
+ /* Tell the parent to suicide. */
98
+ if (noeintr_write(fd[1], &dummy, 1) == -1) {
99
+ warnp("write");
100
+ goto die;
101
+ }
102
+
103
+ /* Close the pipe. */
104
+ while (close(fd[0])) {
105
+ if (errno == EINTR)
106
+ continue;
107
+ warnp("close");
108
+ goto die;
109
+ }
110
+ while (close(fd[1])) {
111
+ if (errno == EINTR)
112
+ continue;
113
+ warnp("close");
114
+ goto die;
115
+ }
116
+
117
+ /* Success! */
118
+ return (0);
119
+
120
+ err2:
121
+ close(fd[1]);
122
+ err1:
123
+ close(fd[0]);
124
+ err0:
125
+ /* Failure! */
126
+ return (-1);
127
+
128
+ die:
129
+ /*
130
+ * We're in the child and something bad happened; the parent will be
131
+ * notified when we die thanks to the pipe being closed.
132
+ */
133
+ _exit(0);
134
+ }
@@ -0,0 +1,10 @@
1
+ #ifndef _DAEMONIZE_H_
2
+ #define _DAEMONIZE_H_
3
+
4
+ /**
5
+ * daemonize(spid):
6
+ * Daemonize and write the process ID in decimal to a file named ${spid}.
7
+ */
8
+ int daemonize(const char *);
9
+
10
+ #endif /* !_DAEMONIZE_H_ */
@@ -0,0 +1,76 @@
1
+ #include <fcntl.h>
2
+ #include <limits.h>
3
+ #include <stdint.h>
4
+ #include <unistd.h>
5
+
6
+ #include "warnp.h"
7
+
8
+ #include "entropy.h"
9
+
10
+ /**
11
+ * XXX Portability
12
+ * XXX We obtain random bytes from the operating system by opening
13
+ * XXX /dev/urandom and reading them from that device; this works on
14
+ * XXX modern UNIX-like operating systems but not on systems like
15
+ * XXX win32 where there is no concept of /dev/urandom.
16
+ */
17
+
18
+ /**
19
+ * entropy_read(buf, buflen):
20
+ * Fill the given buffer with random bytes provided by the operating system.
21
+ */
22
+ int
23
+ entropy_read(uint8_t * buf, size_t buflen)
24
+ {
25
+ int fd;
26
+ ssize_t lenread;
27
+
28
+ /* Sanity-check the buffer size. */
29
+ if (buflen > SSIZE_MAX) {
30
+ warn0("Programmer error: "
31
+ "Trying to read insane amount of random data: %zu",
32
+ buflen);
33
+ goto err0;
34
+ }
35
+
36
+ /* Open /dev/urandom. */
37
+ if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
38
+ warnp("open(/dev/urandom)");
39
+ goto err0;
40
+ }
41
+
42
+ /* Read bytes until we have filled the buffer. */
43
+ while (buflen > 0) {
44
+ if ((lenread = read(fd, buf, buflen)) == -1) {
45
+ warnp("read(/dev/urandom)");
46
+ goto err1;
47
+ }
48
+
49
+ /* The random device should never EOF. */
50
+ if (lenread == 0) {
51
+ warn0("EOF on /dev/urandom?");
52
+ goto err1;
53
+ }
54
+
55
+ /* We've filled a portion of the buffer. */
56
+ buf += lenread;
57
+ buflen -= lenread;
58
+ }
59
+
60
+ /* Close the device. */
61
+ while (close(fd) == -1) {
62
+ if (errno != EINTR) {
63
+ warnp("close(/dev/urandom)");
64
+ goto err0;
65
+ }
66
+ }
67
+
68
+ /* Success! */
69
+ return (0);
70
+
71
+ err1:
72
+ close(fd);
73
+ err0:
74
+ /* Failure! */
75
+ return (-1);
76
+ }