diff options
Diffstat (limited to 'src/exit/gnunet-helper-exit.c')
-rw-r--r-- | src/exit/gnunet-helper-exit.c | 957 |
1 files changed, 478 insertions, 479 deletions
diff --git a/src/exit/gnunet-helper-exit.c b/src/exit/gnunet-helper-exit.c index 297a17813..ca187afa6 100644 --- a/src/exit/gnunet-helper-exit.c +++ b/src/exit/gnunet-helper-exit.c | |||
@@ -81,7 +81,8 @@ static const char *sbin_iptables; | |||
81 | /** | 81 | /** |
82 | * This is in linux/include/net/ipv6.h, but not always exported... | 82 | * This is in linux/include/net/ipv6.h, but not always exported... |
83 | */ | 83 | */ |
84 | struct in6_ifreq { | 84 | struct in6_ifreq |
85 | { | ||
85 | struct in6_addr ifr6_addr; | 86 | struct in6_addr ifr6_addr; |
86 | uint32_t ifr6_prefixlen; /* __u32 in the original */ | 87 | uint32_t ifr6_prefixlen; /* __u32 in the original */ |
87 | int ifr6_ifindex; | 88 | int ifr6_ifindex; |
@@ -97,22 +98,22 @@ struct in6_ifreq { | |||
97 | * @param flags open flags (O_RDONLY, O_WRONLY) | 98 | * @param flags open flags (O_RDONLY, O_WRONLY) |
98 | */ | 99 | */ |
99 | static void | 100 | static void |
100 | open_dev_null(int target_fd, | 101 | open_dev_null (int target_fd, |
101 | int flags) | 102 | int flags) |
102 | { | 103 | { |
103 | int fd; | 104 | int fd; |
104 | 105 | ||
105 | fd = open("/dev/null", flags); | 106 | fd = open ("/dev/null", flags); |
106 | if (-1 == fd) | 107 | if (-1 == fd) |
107 | abort(); | 108 | abort (); |
108 | if (fd == target_fd) | 109 | if (fd == target_fd) |
109 | return; | 110 | return; |
110 | if (-1 == dup2(fd, target_fd)) | 111 | if (-1 == dup2 (fd, target_fd)) |
111 | { | 112 | { |
112 | (void)close(fd); | 113 | (void) close (fd); |
113 | abort(); | 114 | abort (); |
114 | } | 115 | } |
115 | (void)close(fd); | 116 | (void) close (fd); |
116 | } | 117 | } |
117 | 118 | ||
118 | 119 | ||
@@ -124,50 +125,50 @@ open_dev_null(int target_fd, | |||
124 | * @return 0 on success, 1 on any error | 125 | * @return 0 on success, 1 on any error |
125 | */ | 126 | */ |
126 | static int | 127 | static int |
127 | fork_and_exec(const char *file, | 128 | fork_and_exec (const char *file, |
128 | char *const cmd[]) | 129 | char *const cmd[]) |
129 | { | 130 | { |
130 | int status; | 131 | int status; |
131 | pid_t pid; | 132 | pid_t pid; |
132 | pid_t ret; | 133 | pid_t ret; |
133 | 134 | ||
134 | pid = fork(); | 135 | pid = fork (); |
135 | if (-1 == pid) | 136 | if (-1 == pid) |
136 | { | 137 | { |
137 | fprintf(stderr, | 138 | fprintf (stderr, |
138 | "fork failed: %s\n", | 139 | "fork failed: %s\n", |
139 | strerror(errno)); | 140 | strerror (errno)); |
140 | return 1; | 141 | return 1; |
141 | } | 142 | } |
142 | if (0 == pid) | 143 | if (0 == pid) |
143 | { | 144 | { |
144 | /* we are the child process */ | 145 | /* we are the child process */ |
145 | /* close stdin/stdout to not cause interference | 146 | /* close stdin/stdout to not cause interference |
146 | with the helper's main protocol! */ | 147 | with the helper's main protocol! */ |
147 | (void)close(0); | 148 | (void) close (0); |
148 | open_dev_null(0, O_RDONLY); | 149 | open_dev_null (0, O_RDONLY); |
149 | (void)close(1); | 150 | (void) close (1); |
150 | open_dev_null(1, O_WRONLY); | 151 | open_dev_null (1, O_WRONLY); |
151 | (void)execv(file, cmd); | 152 | (void) execv (file, cmd); |
152 | /* can only get here on error */ | 153 | /* can only get here on error */ |
153 | fprintf(stderr, | 154 | fprintf (stderr, |
154 | "exec `%s' failed: %s\n", | 155 | "exec `%s' failed: %s\n", |
155 | file, | 156 | file, |
156 | strerror(errno)); | 157 | strerror (errno)); |
157 | _exit(1); | 158 | _exit (1); |
158 | } | 159 | } |
159 | /* keep running waitpid as long as the only error we get is 'EINTR' */ | 160 | /* keep running waitpid as long as the only error we get is 'EINTR' */ |
160 | while ((-1 == (ret = waitpid(pid, &status, 0))) && | 161 | while ((-1 == (ret = waitpid (pid, &status, 0))) && |
161 | (errno == EINTR)) | 162 | (errno == EINTR)) |
162 | ; | 163 | ; |
163 | if (-1 == ret) | 164 | if (-1 == ret) |
164 | { | 165 | { |
165 | fprintf(stderr, | 166 | fprintf (stderr, |
166 | "waitpid failed: %s\n", | 167 | "waitpid failed: %s\n", |
167 | strerror(errno)); | 168 | strerror (errno)); |
168 | return 1; | 169 | return 1; |
169 | } | 170 | } |
170 | if (!(WIFEXITED(status) && (0 == WEXITSTATUS(status)))) | 171 | if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status)))) |
171 | return 1; | 172 | return 1; |
172 | /* child process completed and returned success, we're happy */ | 173 | /* child process completed and returned success, we're happy */ |
173 | return 0; | 174 | return 0; |
@@ -182,46 +183,46 @@ fork_and_exec(const char *file, | |||
182 | * @return the fd to the tun or -1 on error | 183 | * @return the fd to the tun or -1 on error |
183 | */ | 184 | */ |
184 | static int | 185 | static int |
185 | init_tun(char *dev) | 186 | init_tun (char *dev) |
186 | { | 187 | { |
187 | struct ifreq ifr; | 188 | struct ifreq ifr; |
188 | int fd; | 189 | int fd; |
189 | 190 | ||
190 | if (NULL == dev) | 191 | if (NULL == dev) |
191 | { | 192 | { |
192 | errno = EINVAL; | 193 | errno = EINVAL; |
193 | return -1; | 194 | return -1; |
194 | } | 195 | } |
195 | 196 | ||
196 | if (-1 == (fd = open("/dev/net/tun", O_RDWR))) | 197 | if (-1 == (fd = open ("/dev/net/tun", O_RDWR))) |
197 | { | 198 | { |
198 | fprintf(stderr, "Error opening `%s': %s\n", "/dev/net/tun", | 199 | fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun", |
199 | strerror(errno)); | 200 | strerror (errno)); |
200 | return -1; | 201 | return -1; |
201 | } | 202 | } |
202 | 203 | ||
203 | if (fd >= FD_SETSIZE) | 204 | if (fd >= FD_SETSIZE) |
204 | { | 205 | { |
205 | fprintf(stderr, "File descriptor to large: %d", fd); | 206 | fprintf (stderr, "File descriptor to large: %d", fd); |
206 | (void)close(fd); | 207 | (void) close (fd); |
207 | return -1; | 208 | return -1; |
208 | } | 209 | } |
209 | 210 | ||
210 | memset(&ifr, 0, sizeof(ifr)); | 211 | memset (&ifr, 0, sizeof(ifr)); |
211 | ifr.ifr_flags = IFF_TUN; | 212 | ifr.ifr_flags = IFF_TUN; |
212 | 213 | ||
213 | if ('\0' != *dev) | 214 | if ('\0' != *dev) |
214 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); | 215 | strncpy (ifr.ifr_name, dev, IFNAMSIZ); |
215 | 216 | ||
216 | if (-1 == ioctl(fd, TUNSETIFF, (void *)&ifr)) | 217 | if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr)) |
217 | { | 218 | { |
218 | fprintf(stderr, | 219 | fprintf (stderr, |
219 | "Error with ioctl on `%s': %s\n", "/dev/net/tun", | 220 | "Error with ioctl on `%s': %s\n", "/dev/net/tun", |
220 | strerror(errno)); | 221 | strerror (errno)); |
221 | (void)close(fd); | 222 | (void) close (fd); |
222 | return -1; | 223 | return -1; |
223 | } | 224 | } |
224 | strcpy(dev, ifr.ifr_name); | 225 | strcpy (dev, ifr.ifr_name); |
225 | return fd; | 226 | return fd; |
226 | } | 227 | } |
227 | 228 | ||
@@ -234,7 +235,7 @@ init_tun(char *dev) | |||
234 | * @param prefix_len the length of the network-prefix | 235 | * @param prefix_len the length of the network-prefix |
235 | */ | 236 | */ |
236 | static void | 237 | static void |
237 | set_address6(const char *dev, const char *address, unsigned long prefix_len) | 238 | set_address6 (const char *dev, const char *address, unsigned long prefix_len) |
238 | { | 239 | { |
239 | struct ifreq ifr; | 240 | struct ifreq ifr; |
240 | struct sockaddr_in6 sa6; | 241 | struct sockaddr_in6 sa6; |
@@ -244,34 +245,34 @@ set_address6(const char *dev, const char *address, unsigned long prefix_len) | |||
244 | /* | 245 | /* |
245 | * parse the new address | 246 | * parse the new address |
246 | */ | 247 | */ |
247 | memset(&sa6, 0, sizeof(struct sockaddr_in6)); | 248 | memset (&sa6, 0, sizeof(struct sockaddr_in6)); |
248 | sa6.sin6_family = AF_INET6; | 249 | sa6.sin6_family = AF_INET6; |
249 | if (1 != inet_pton(AF_INET6, address, &sa6.sin6_addr)) | 250 | if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr)) |
250 | { | 251 | { |
251 | fprintf(stderr, "Failed to parse address `%s': %s\n", address, | 252 | fprintf (stderr, "Failed to parse address `%s': %s\n", address, |
252 | strerror(errno)); | 253 | strerror (errno)); |
253 | exit(1); | 254 | exit (1); |
254 | } | 255 | } |
255 | 256 | ||
256 | if (-1 == (fd = socket(PF_INET6, SOCK_DGRAM, 0))) | 257 | if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0))) |
257 | { | 258 | { |
258 | fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); | 259 | fprintf (stderr, "Error creating socket: %s\n", strerror (errno)); |
259 | exit(1); | 260 | exit (1); |
260 | } | 261 | } |
261 | 262 | ||
262 | memset(&ifr, 0, sizeof(struct ifreq)); | 263 | memset (&ifr, 0, sizeof(struct ifreq)); |
263 | /* | 264 | /* |
264 | * Get the index of the if | 265 | * Get the index of the if |
265 | */ | 266 | */ |
266 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); | 267 | strncpy (ifr.ifr_name, dev, IFNAMSIZ); |
267 | if (-1 == ioctl(fd, SIOGIFINDEX, &ifr)) | 268 | if (-1 == ioctl (fd, SIOGIFINDEX, &ifr)) |
268 | { | 269 | { |
269 | fprintf(stderr, "ioctl failed at %d: %s\n", __LINE__, strerror(errno)); | 270 | fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno)); |
270 | (void)close(fd); | 271 | (void) close (fd); |
271 | exit(1); | 272 | exit (1); |
272 | } | 273 | } |
273 | 274 | ||
274 | memset(&ifr6, 0, sizeof(struct in6_ifreq)); | 275 | memset (&ifr6, 0, sizeof(struct in6_ifreq)); |
275 | ifr6.ifr6_addr = sa6.sin6_addr; | 276 | ifr6.ifr6_addr = sa6.sin6_addr; |
276 | ifr6.ifr6_ifindex = ifr.ifr_ifindex; | 277 | ifr6.ifr6_ifindex = ifr.ifr_ifindex; |
277 | ifr6.ifr6_prefixlen = prefix_len; | 278 | ifr6.ifr6_prefixlen = prefix_len; |
@@ -279,42 +280,42 @@ set_address6(const char *dev, const char *address, unsigned long prefix_len) | |||
279 | /* | 280 | /* |
280 | * Set the address | 281 | * Set the address |
281 | */ | 282 | */ |
282 | if (-1 == ioctl(fd, SIOCSIFADDR, &ifr6)) | 283 | if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6)) |
283 | { | 284 | { |
284 | fprintf(stderr, "ioctl failed at line %d: %s\n", __LINE__, | 285 | fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__, |
285 | strerror(errno)); | 286 | strerror (errno)); |
286 | (void)close(fd); | 287 | (void) close (fd); |
287 | exit(1); | 288 | exit (1); |
288 | } | 289 | } |
289 | 290 | ||
290 | /* | 291 | /* |
291 | * Get the flags | 292 | * Get the flags |
292 | */ | 293 | */ |
293 | if (-1 == ioctl(fd, SIOCGIFFLAGS, &ifr)) | 294 | if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr)) |
294 | { | 295 | { |
295 | fprintf(stderr, "ioctl failed at line %d: %s\n", __LINE__, | 296 | fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__, |
296 | strerror(errno)); | 297 | strerror (errno)); |
297 | (void)close(fd); | 298 | (void) close (fd); |
298 | exit(1); | 299 | exit (1); |
299 | } | 300 | } |
300 | 301 | ||
301 | /* | 302 | /* |
302 | * Add the UP and RUNNING flags | 303 | * Add the UP and RUNNING flags |
303 | */ | 304 | */ |
304 | ifr.ifr_flags |= IFF_UP | IFF_RUNNING; | 305 | ifr.ifr_flags |= IFF_UP | IFF_RUNNING; |
305 | if (-1 == ioctl(fd, SIOCSIFFLAGS, &ifr)) | 306 | if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr)) |
306 | { | 307 | { |
307 | fprintf(stderr, "ioctl failed at line %d: %s\n", __LINE__, | 308 | fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__, |
308 | strerror(errno)); | 309 | strerror (errno)); |
309 | (void)close(fd); | 310 | (void) close (fd); |
310 | exit(1); | 311 | exit (1); |
311 | } | 312 | } |
312 | 313 | ||
313 | if (0 != close(fd)) | 314 | if (0 != close (fd)) |
314 | { | 315 | { |
315 | fprintf(stderr, "close failed: %s\n", strerror(errno)); | 316 | fprintf (stderr, "close failed: %s\n", strerror (errno)); |
316 | exit(1); | 317 | exit (1); |
317 | } | 318 | } |
318 | } | 319 | } |
319 | 320 | ||
320 | 321 | ||
@@ -326,96 +327,96 @@ set_address6(const char *dev, const char *address, unsigned long prefix_len) | |||
326 | * @param mask the netmask | 327 | * @param mask the netmask |
327 | */ | 328 | */ |
328 | static void | 329 | static void |
329 | set_address4(const char *dev, const char *address, const char *mask) | 330 | set_address4 (const char *dev, const char *address, const char *mask) |
330 | { | 331 | { |
331 | int fd; | 332 | int fd; |
332 | struct sockaddr_in *addr; | 333 | struct sockaddr_in *addr; |
333 | struct ifreq ifr; | 334 | struct ifreq ifr; |
334 | 335 | ||
335 | memset(&ifr, 0, sizeof(struct ifreq)); | 336 | memset (&ifr, 0, sizeof(struct ifreq)); |
336 | addr = (struct sockaddr_in *)&(ifr.ifr_addr); | 337 | addr = (struct sockaddr_in *) &(ifr.ifr_addr); |
337 | addr->sin_family = AF_INET; | 338 | addr->sin_family = AF_INET; |
338 | 339 | ||
339 | /* | 340 | /* |
340 | * Parse the address | 341 | * Parse the address |
341 | */ | 342 | */ |
342 | if (1 != inet_pton(AF_INET, address, &addr->sin_addr.s_addr)) | 343 | if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr)) |
343 | { | 344 | { |
344 | fprintf(stderr, "Failed to parse address `%s': %s\n", address, | 345 | fprintf (stderr, "Failed to parse address `%s': %s\n", address, |
345 | strerror(errno)); | 346 | strerror (errno)); |
346 | exit(1); | 347 | exit (1); |
347 | } | 348 | } |
348 | 349 | ||
349 | if (-1 == (fd = socket(PF_INET, SOCK_DGRAM, 0))) | 350 | if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0))) |
350 | { | 351 | { |
351 | fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); | 352 | fprintf (stderr, "Error creating socket: %s\n", strerror (errno)); |
352 | exit(1); | 353 | exit (1); |
353 | } | 354 | } |
354 | 355 | ||
355 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); | 356 | strncpy (ifr.ifr_name, dev, IFNAMSIZ); |
356 | 357 | ||
357 | /* | 358 | /* |
358 | * Set the address | 359 | * Set the address |
359 | */ | 360 | */ |
360 | if (-1 == ioctl(fd, SIOCSIFADDR, &ifr)) | 361 | if (-1 == ioctl (fd, SIOCSIFADDR, &ifr)) |
361 | { | 362 | { |
362 | fprintf(stderr, "ioctl failed at %d: %s\n", __LINE__, strerror(errno)); | 363 | fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno)); |
363 | (void)close(fd); | 364 | (void) close (fd); |
364 | exit(1); | 365 | exit (1); |
365 | } | 366 | } |
366 | 367 | ||
367 | /* | 368 | /* |
368 | * Parse the netmask | 369 | * Parse the netmask |
369 | */ | 370 | */ |
370 | addr = (struct sockaddr_in *)&(ifr.ifr_netmask); | 371 | addr = (struct sockaddr_in *) &(ifr.ifr_netmask); |
371 | if (1 != inet_pton(AF_INET, mask, &addr->sin_addr.s_addr)) | 372 | if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr)) |
372 | { | 373 | { |
373 | fprintf(stderr, "Failed to parse address `%s': %s\n", mask, | 374 | fprintf (stderr, "Failed to parse address `%s': %s\n", mask, |
374 | strerror(errno)); | 375 | strerror (errno)); |
375 | (void)close(fd); | 376 | (void) close (fd); |
376 | exit(1); | 377 | exit (1); |
377 | } | 378 | } |
378 | 379 | ||
379 | /* | 380 | /* |
380 | * Set the netmask | 381 | * Set the netmask |
381 | */ | 382 | */ |
382 | if (-1 == ioctl(fd, SIOCSIFNETMASK, &ifr)) | 383 | if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr)) |
383 | { | 384 | { |
384 | fprintf(stderr, "ioctl failed at line %d: %s\n", __LINE__, | 385 | fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__, |
385 | strerror(errno)); | 386 | strerror (errno)); |
386 | (void)close(fd); | 387 | (void) close (fd); |
387 | exit(1); | 388 | exit (1); |
388 | } | 389 | } |
389 | 390 | ||
390 | /* | 391 | /* |
391 | * Get the flags | 392 | * Get the flags |
392 | */ | 393 | */ |
393 | if (-1 == ioctl(fd, SIOCGIFFLAGS, &ifr)) | 394 | if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr)) |
394 | { | 395 | { |
395 | fprintf(stderr, "ioctl failed at line %d: %s\n", __LINE__, | 396 | fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__, |
396 | strerror(errno)); | 397 | strerror (errno)); |
397 | (void)close(fd); | 398 | (void) close (fd); |
398 | exit(1); | 399 | exit (1); |
399 | } | 400 | } |
400 | 401 | ||
401 | /* | 402 | /* |
402 | * Add the UP and RUNNING flags | 403 | * Add the UP and RUNNING flags |
403 | */ | 404 | */ |
404 | ifr.ifr_flags |= IFF_UP | IFF_RUNNING; | 405 | ifr.ifr_flags |= IFF_UP | IFF_RUNNING; |
405 | if (-1 == ioctl(fd, SIOCSIFFLAGS, &ifr)) | 406 | if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr)) |
406 | { | 407 | { |
407 | fprintf(stderr, "ioctl failed at line %d: %s\n", __LINE__, | 408 | fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__, |
408 | strerror(errno)); | 409 | strerror (errno)); |
409 | (void)close(fd); | 410 | (void) close (fd); |
410 | exit(1); | 411 | exit (1); |
411 | } | 412 | } |
412 | 413 | ||
413 | if (0 != close(fd)) | 414 | if (0 != close (fd)) |
414 | { | 415 | { |
415 | fprintf(stderr, "close failed: %s\n", strerror(errno)); | 416 | fprintf (stderr, "close failed: %s\n", strerror (errno)); |
416 | (void)close(fd); | 417 | (void) close (fd); |
417 | exit(1); | 418 | exit (1); |
418 | } | 419 | } |
419 | } | 420 | } |
420 | 421 | ||
421 | 422 | ||
@@ -425,7 +426,7 @@ set_address4(const char *dev, const char *address, const char *mask) | |||
425 | * @param fd_tun tunnel FD | 426 | * @param fd_tun tunnel FD |
426 | */ | 427 | */ |
427 | static void | 428 | static void |
428 | run(int fd_tun) | 429 | run (int fd_tun) |
429 | { | 430 | { |
430 | /* | 431 | /* |
431 | * The buffer filled by reading from fd_tun | 432 | * The buffer filled by reading from fd_tun |
@@ -452,188 +453,188 @@ run(int fd_tun) | |||
452 | int write_open = 1; | 453 | int write_open = 1; |
453 | 454 | ||
454 | while ((1 == read_open) && (1 == write_open)) | 455 | while ((1 == read_open) && (1 == write_open)) |
455 | { | 456 | { |
456 | FD_ZERO(&fds_w); | 457 | FD_ZERO (&fds_w); |
457 | FD_ZERO(&fds_r); | 458 | FD_ZERO (&fds_r); |
458 | 459 | ||
459 | /* | 460 | /* |
460 | * We are supposed to read and the buffer is empty | 461 | * We are supposed to read and the buffer is empty |
461 | * -> select on read from tun | 462 | * -> select on read from tun |
462 | */ | 463 | */ |
463 | if (read_open && (0 == buftun_size)) | 464 | if (read_open && (0 == buftun_size)) |
464 | FD_SET(fd_tun, &fds_r); | 465 | FD_SET (fd_tun, &fds_r); |
465 | 466 | ||
466 | /* | 467 | /* |
467 | * We are supposed to read and the buffer is not empty | 468 | * We are supposed to read and the buffer is not empty |
468 | * -> select on write to stdout | 469 | * -> select on write to stdout |
469 | */ | 470 | */ |
470 | if (read_open && (0 != buftun_size)) | 471 | if (read_open && (0 != buftun_size)) |
471 | FD_SET(1, &fds_w); | 472 | FD_SET (1, &fds_w); |
472 | 473 | ||
473 | /* | 474 | /* |
474 | * We are supposed to write and the buffer is empty | 475 | * We are supposed to write and the buffer is empty |
475 | * -> select on read from stdin | 476 | * -> select on read from stdin |
476 | */ | 477 | */ |
477 | if (write_open && (NULL == bufin_read)) | 478 | if (write_open && (NULL == bufin_read)) |
478 | FD_SET(0, &fds_r); | 479 | FD_SET (0, &fds_r); |
479 | 480 | ||
480 | /* | 481 | /* |
481 | * We are supposed to write and the buffer is not empty | 482 | * We are supposed to write and the buffer is not empty |
482 | * -> select on write to tun | 483 | * -> select on write to tun |
483 | */ | 484 | */ |
484 | if (write_open && (NULL != bufin_read)) | 485 | if (write_open && (NULL != bufin_read)) |
485 | FD_SET(fd_tun, &fds_w); | 486 | FD_SET (fd_tun, &fds_w); |
486 | 487 | ||
487 | int r = select(fd_tun + 1, &fds_r, &fds_w, NULL, NULL); | 488 | int r = select (fd_tun + 1, &fds_r, &fds_w, NULL, NULL); |
488 | 489 | ||
489 | if (-1 == r) | 490 | if (-1 == r) |
491 | { | ||
492 | if (EINTR == errno) | ||
493 | continue; | ||
494 | fprintf (stderr, "select failed: %s\n", strerror (errno)); | ||
495 | exit (1); | ||
496 | } | ||
497 | |||
498 | if (r > 0) | ||
499 | { | ||
500 | if (FD_ISSET (fd_tun, &fds_r)) | ||
501 | { | ||
502 | buftun_size = | ||
503 | read (fd_tun, buftun + sizeof(struct GNUNET_MessageHeader), | ||
504 | MAX_SIZE - sizeof(struct GNUNET_MessageHeader)); | ||
505 | if (-1 == buftun_size) | ||
490 | { | 506 | { |
491 | if (EINTR == errno) | 507 | fprintf (stderr, |
492 | continue; | 508 | "read-error: %s\n", |
493 | fprintf(stderr, "select failed: %s\n", strerror(errno)); | 509 | strerror (errno)); |
494 | exit(1); | 510 | shutdown (fd_tun, SHUT_RD); |
511 | shutdown (1, SHUT_WR); | ||
512 | read_open = 0; | ||
513 | buftun_size = 0; | ||
495 | } | 514 | } |
496 | 515 | else if (0 == buftun_size) | |
497 | if (r > 0) | ||
498 | { | 516 | { |
499 | if (FD_ISSET(fd_tun, &fds_r)) | ||
500 | { | ||
501 | buftun_size = | ||
502 | read(fd_tun, buftun + sizeof(struct GNUNET_MessageHeader), | ||
503 | MAX_SIZE - sizeof(struct GNUNET_MessageHeader)); | ||
504 | if (-1 == buftun_size) | ||
505 | { | ||
506 | fprintf(stderr, | ||
507 | "read-error: %s\n", | ||
508 | strerror(errno)); | ||
509 | shutdown(fd_tun, SHUT_RD); | ||
510 | shutdown(1, SHUT_WR); | ||
511 | read_open = 0; | ||
512 | buftun_size = 0; | ||
513 | } | ||
514 | else if (0 == buftun_size) | ||
515 | { | ||
516 | #if DEBUG | 517 | #if DEBUG |
517 | fprintf(stderr, "EOF on tun\n"); | 518 | fprintf (stderr, "EOF on tun\n"); |
518 | #endif | 519 | #endif |
519 | shutdown(fd_tun, SHUT_RD); | 520 | shutdown (fd_tun, SHUT_RD); |
520 | shutdown(1, SHUT_WR); | 521 | shutdown (1, SHUT_WR); |
521 | read_open = 0; | 522 | read_open = 0; |
522 | buftun_size = 0; | 523 | buftun_size = 0; |
523 | } | 524 | } |
524 | else | 525 | else |
525 | { | 526 | { |
526 | buftun_read = buftun; | 527 | buftun_read = buftun; |
527 | struct GNUNET_MessageHeader *hdr = | 528 | struct GNUNET_MessageHeader *hdr = |
528 | (struct GNUNET_MessageHeader *)buftun; | 529 | (struct GNUNET_MessageHeader *) buftun; |
529 | buftun_size += sizeof(struct GNUNET_MessageHeader); | 530 | buftun_size += sizeof(struct GNUNET_MessageHeader); |
530 | hdr->type = htons(GNUNET_MESSAGE_TYPE_VPN_HELPER); | 531 | hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); |
531 | hdr->size = htons(buftun_size); | 532 | hdr->size = htons (buftun_size); |
532 | } | 533 | } |
533 | } | 534 | } |
534 | else if (FD_ISSET(1, &fds_w)) | 535 | else if (FD_ISSET (1, &fds_w)) |
535 | { | 536 | { |
536 | ssize_t written = write(1, buftun_read, buftun_size); | 537 | ssize_t written = write (1, buftun_read, buftun_size); |
537 | 538 | ||
538 | if (-1 == written) | 539 | if (-1 == written) |
539 | { | 540 | { |
540 | #if !DEBUG | 541 | #if ! DEBUG |
541 | if (errno != EPIPE) | 542 | if (errno != EPIPE) |
542 | #endif | 543 | #endif |
543 | fprintf(stderr, | 544 | fprintf (stderr, |
544 | "write-error to stdout: %s\n", | 545 | "write-error to stdout: %s\n", |
545 | strerror(errno)); | 546 | strerror (errno)); |
546 | shutdown(fd_tun, SHUT_RD); | 547 | shutdown (fd_tun, SHUT_RD); |
547 | shutdown(1, SHUT_WR); | 548 | shutdown (1, SHUT_WR); |
548 | read_open = 0; | 549 | read_open = 0; |
549 | buftun_size = 0; | 550 | buftun_size = 0; |
550 | } | 551 | } |
551 | else if (0 == written) | 552 | else if (0 == written) |
552 | { | 553 | { |
553 | fprintf(stderr, "write returned 0!?\n"); | 554 | fprintf (stderr, "write returned 0!?\n"); |
554 | exit(1); | 555 | exit (1); |
555 | } | 556 | } |
556 | else | 557 | else |
557 | { | 558 | { |
558 | buftun_size -= written; | 559 | buftun_size -= written; |
559 | buftun_read += written; | 560 | buftun_read += written; |
560 | } | 561 | } |
561 | } | 562 | } |
562 | 563 | ||
563 | if (FD_ISSET(0, &fds_r)) | 564 | if (FD_ISSET (0, &fds_r)) |
564 | { | 565 | { |
565 | bufin_size = read(0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos); | 566 | bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos); |
566 | if (-1 == bufin_size) | 567 | if (-1 == bufin_size) |
567 | { | 568 | { |
568 | fprintf(stderr, "read-error: %s\n", strerror(errno)); | 569 | fprintf (stderr, "read-error: %s\n", strerror (errno)); |
569 | shutdown(0, SHUT_RD); | 570 | shutdown (0, SHUT_RD); |
570 | shutdown(fd_tun, SHUT_WR); | 571 | shutdown (fd_tun, SHUT_WR); |
571 | write_open = 0; | 572 | write_open = 0; |
572 | bufin_size = 0; | 573 | bufin_size = 0; |
573 | } | 574 | } |
574 | else if (0 == bufin_size) | 575 | else if (0 == bufin_size) |
575 | { | 576 | { |
576 | #if DEBUG | 577 | #if DEBUG |
577 | fprintf(stderr, "EOF on stdin\n"); | 578 | fprintf (stderr, "EOF on stdin\n"); |
578 | #endif | 579 | #endif |
579 | shutdown(0, SHUT_RD); | 580 | shutdown (0, SHUT_RD); |
580 | shutdown(fd_tun, SHUT_WR); | 581 | shutdown (fd_tun, SHUT_WR); |
581 | write_open = 0; | 582 | write_open = 0; |
582 | bufin_size = 0; | 583 | bufin_size = 0; |
583 | } | 584 | } |
584 | else | 585 | else |
585 | { | 586 | { |
586 | struct GNUNET_MessageHeader *hdr; | 587 | struct GNUNET_MessageHeader *hdr; |
587 | 588 | ||
588 | PROCESS_BUFFER: | 589 | PROCESS_BUFFER: |
589 | bufin_rpos += bufin_size; | 590 | bufin_rpos += bufin_size; |
590 | if (bufin_rpos < sizeof(struct GNUNET_MessageHeader)) | 591 | if (bufin_rpos < sizeof(struct GNUNET_MessageHeader)) |
591 | continue; | 592 | continue; |
592 | hdr = (struct GNUNET_MessageHeader *)bufin; | 593 | hdr = (struct GNUNET_MessageHeader *) bufin; |
593 | if (ntohs(hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) | 594 | if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) |
594 | { | 595 | { |
595 | fprintf(stderr, "protocol violation!\n"); | 596 | fprintf (stderr, "protocol violation!\n"); |
596 | exit(1); | 597 | exit (1); |
597 | } | 598 | } |
598 | if (ntohs(hdr->size) > bufin_rpos) | 599 | if (ntohs (hdr->size) > bufin_rpos) |
599 | continue; | 600 | continue; |
600 | bufin_read = bufin + sizeof(struct GNUNET_MessageHeader); | 601 | bufin_read = bufin + sizeof(struct GNUNET_MessageHeader); |
601 | bufin_size = ntohs(hdr->size) - sizeof(struct GNUNET_MessageHeader); | 602 | bufin_size = ntohs (hdr->size) - sizeof(struct GNUNET_MessageHeader); |
602 | bufin_rpos -= bufin_size + sizeof(struct GNUNET_MessageHeader); | 603 | bufin_rpos -= bufin_size + sizeof(struct GNUNET_MessageHeader); |
603 | } | 604 | } |
604 | } | 605 | } |
605 | else if (FD_ISSET(fd_tun, &fds_w)) | 606 | else if (FD_ISSET (fd_tun, &fds_w)) |
606 | { | 607 | { |
607 | ssize_t written = write(fd_tun, bufin_read, bufin_size); | 608 | ssize_t written = write (fd_tun, bufin_read, bufin_size); |
608 | 609 | ||
609 | if (-1 == written) | 610 | if (-1 == written) |
610 | { | 611 | { |
611 | fprintf(stderr, "write-error to tun: %s\n", strerror(errno)); | 612 | fprintf (stderr, "write-error to tun: %s\n", strerror (errno)); |
612 | shutdown(0, SHUT_RD); | 613 | shutdown (0, SHUT_RD); |
613 | shutdown(fd_tun, SHUT_WR); | 614 | shutdown (fd_tun, SHUT_WR); |
614 | write_open = 0; | 615 | write_open = 0; |
615 | bufin_size = 0; | 616 | bufin_size = 0; |
616 | } | ||
617 | else if (0 == written) | ||
618 | { | ||
619 | fprintf(stderr, "write returned 0!?\n"); | ||
620 | exit(1); | ||
621 | } | ||
622 | else | ||
623 | { | ||
624 | bufin_size -= written; | ||
625 | bufin_read += written; | ||
626 | if (0 == bufin_size) | ||
627 | { | ||
628 | memmove(bufin, bufin_read, bufin_rpos); | ||
629 | bufin_read = NULL; /* start reading again */ | ||
630 | bufin_size = 0; | ||
631 | goto PROCESS_BUFFER; | ||
632 | } | ||
633 | } | ||
634 | } | ||
635 | } | 617 | } |
618 | else if (0 == written) | ||
619 | { | ||
620 | fprintf (stderr, "write returned 0!?\n"); | ||
621 | exit (1); | ||
622 | } | ||
623 | else | ||
624 | { | ||
625 | bufin_size -= written; | ||
626 | bufin_read += written; | ||
627 | if (0 == bufin_size) | ||
628 | { | ||
629 | memmove (bufin, bufin_read, bufin_rpos); | ||
630 | bufin_read = NULL; /* start reading again */ | ||
631 | bufin_size = 0; | ||
632 | goto PROCESS_BUFFER; | ||
633 | } | ||
634 | } | ||
635 | } | ||
636 | } | 636 | } |
637 | } | ||
637 | } | 638 | } |
638 | 639 | ||
639 | 640 | ||
@@ -651,166 +652,164 @@ PROCESS_BUFFER: | |||
651 | * 6: IPv4 netmask ("255.255.0.0") [ignored if #4 is "-"] | 652 | * 6: IPv4 netmask ("255.255.0.0") [ignored if #4 is "-"] |
652 | */ | 653 | */ |
653 | int | 654 | int |
654 | main(int argc, char **argv) | 655 | main (int argc, char **argv) |
655 | { | 656 | { |
656 | char dev[IFNAMSIZ]; | 657 | char dev[IFNAMSIZ]; |
657 | int fd_tun; | 658 | int fd_tun; |
658 | int global_ret; | 659 | int global_ret; |
659 | 660 | ||
660 | if (7 != argc) | 661 | if (7 != argc) |
661 | { | 662 | { |
662 | fprintf(stderr, "Fatal: must supply 6 arguments!\n"); | 663 | fprintf (stderr, "Fatal: must supply 6 arguments!\n"); |
663 | return 1; | 664 | return 1; |
664 | } | 665 | } |
665 | if ((0 == strcmp(argv[3], "-")) && | 666 | if ((0 == strcmp (argv[3], "-")) && |
666 | (0 == strcmp(argv[5], "-"))) | 667 | (0 == strcmp (argv[5], "-"))) |
667 | { | 668 | { |
668 | fprintf(stderr, "Fatal: disabling both IPv4 and IPv6 makes no sense.\n"); | 669 | fprintf (stderr, "Fatal: disabling both IPv4 and IPv6 makes no sense.\n"); |
669 | return 1; | 670 | return 1; |
670 | } | 671 | } |
671 | if (0 != strcmp(argv[2], "-")) | 672 | if (0 != strcmp (argv[2], "-")) |
672 | { | 673 | { |
673 | #ifdef IPTABLES | 674 | #ifdef IPTABLES |
674 | if (0 == access(IPTABLES, X_OK)) | 675 | if (0 == access (IPTABLES, X_OK)) |
675 | sbin_iptables = IPTABLES; | 676 | sbin_iptables = IPTABLES; |
676 | else | 677 | else |
677 | #endif | 678 | #endif |
678 | if (0 == access("/sbin/iptables", X_OK)) | 679 | if (0 == access ("/sbin/iptables", X_OK)) |
679 | sbin_iptables = "/sbin/iptables"; | 680 | sbin_iptables = "/sbin/iptables"; |
680 | else if (0 == access("/usr/sbin/iptables", X_OK)) | 681 | else if (0 == access ("/usr/sbin/iptables", X_OK)) |
681 | sbin_iptables = "/usr/sbin/iptables"; | 682 | sbin_iptables = "/usr/sbin/iptables"; |
682 | else | 683 | else |
683 | { | 684 | { |
684 | fprintf(stderr, | 685 | fprintf (stderr, |
685 | "Fatal: executable iptables not found in approved directories: %s\n", | 686 | "Fatal: executable iptables not found in approved directories: %s\n", |
686 | strerror(errno)); | 687 | strerror (errno)); |
687 | return 1; | 688 | return 1; |
688 | } | 689 | } |
689 | #ifdef SYSCTL | 690 | #ifdef SYSCTL |
690 | if (0 == access(SYSCTL, X_OK)) | 691 | if (0 == access (SYSCTL, X_OK)) |
691 | sbin_sysctl = SYSCTL; | 692 | sbin_sysctl = SYSCTL; |
692 | else | 693 | else |
693 | #endif | 694 | #endif |
694 | if (0 == access("/sbin/sysctl", X_OK)) | 695 | if (0 == access ("/sbin/sysctl", X_OK)) |
695 | sbin_sysctl = "/sbin/sysctl"; | 696 | sbin_sysctl = "/sbin/sysctl"; |
696 | else if (0 == access("/usr/sbin/sysctl", X_OK)) | 697 | else if (0 == access ("/usr/sbin/sysctl", X_OK)) |
697 | sbin_sysctl = "/usr/sbin/sysctl"; | 698 | sbin_sysctl = "/usr/sbin/sysctl"; |
698 | else | 699 | else |
699 | { | 700 | { |
700 | fprintf(stderr, | 701 | fprintf (stderr, |
701 | "Fatal: executable sysctl not found in approved directories: %s\n", | 702 | "Fatal: executable sysctl not found in approved directories: %s\n", |
702 | strerror(errno)); | 703 | strerror (errno)); |
703 | return 1; | 704 | return 1; |
704 | } | ||
705 | } | 705 | } |
706 | } | ||
706 | 707 | ||
707 | strncpy(dev, argv[1], IFNAMSIZ); | 708 | strncpy (dev, argv[1], IFNAMSIZ); |
708 | dev[IFNAMSIZ - 1] = '\0'; | 709 | dev[IFNAMSIZ - 1] = '\0'; |
709 | 710 | ||
710 | if (-1 == (fd_tun = init_tun(dev))) | 711 | if (-1 == (fd_tun = init_tun (dev))) |
712 | { | ||
713 | fprintf (stderr, | ||
714 | "Fatal: could not initialize tun-interface `%s' with IPv6 %s/%s and IPv4 %s/%s\n", | ||
715 | dev, | ||
716 | argv[3], | ||
717 | argv[4], | ||
718 | argv[5], | ||
719 | argv[6]); | ||
720 | return 1; | ||
721 | } | ||
722 | |||
723 | if (0 != strcmp (argv[3], "-")) | ||
724 | { | ||
711 | { | 725 | { |
712 | fprintf(stderr, | 726 | const char *address = argv[3]; |
713 | "Fatal: could not initialize tun-interface `%s' with IPv6 %s/%s and IPv4 %s/%s\n", | 727 | long prefix_len = atol (argv[4]); |
714 | dev, | ||
715 | argv[3], | ||
716 | argv[4], | ||
717 | argv[5], | ||
718 | argv[6]); | ||
719 | return 1; | ||
720 | } | ||
721 | 728 | ||
722 | if (0 != strcmp(argv[3], "-")) | 729 | if ((prefix_len < 1) || (prefix_len > 127)) |
730 | { | ||
731 | fprintf (stderr, "Fatal: prefix_len out of range\n"); | ||
732 | return 1; | ||
733 | } | ||
734 | set_address6 (dev, address, prefix_len); | ||
735 | } | ||
736 | if (0 != strcmp (argv[2], "-")) | ||
723 | { | 737 | { |
738 | char *const sysctl_args[] = { | ||
739 | "sysctl", "-w", "net.ipv6.conf.all.forwarding=1", NULL | ||
740 | }; | ||
741 | if (0 != fork_and_exec (sbin_sysctl, | ||
742 | sysctl_args)) | ||
724 | { | 743 | { |
725 | const char *address = argv[3]; | 744 | fprintf (stderr, |
726 | long prefix_len = atol(argv[4]); | 745 | "Failed to enable IPv6 forwarding. Will continue anyway.\n"); |
727 | |||
728 | if ((prefix_len < 1) || (prefix_len > 127)) | ||
729 | { | ||
730 | fprintf(stderr, "Fatal: prefix_len out of range\n"); | ||
731 | return 1; | ||
732 | } | ||
733 | set_address6(dev, address, prefix_len); | ||
734 | } | 746 | } |
735 | if (0 != strcmp(argv[2], "-")) | ||
736 | { | ||
737 | char *const sysctl_args[] = | ||
738 | { | ||
739 | "sysctl", "-w", "net.ipv6.conf.all.forwarding=1", NULL | ||
740 | }; | ||
741 | if (0 != fork_and_exec(sbin_sysctl, | ||
742 | sysctl_args)) | ||
743 | { | ||
744 | fprintf(stderr, | ||
745 | "Failed to enable IPv6 forwarding. Will continue anyway.\n"); | ||
746 | } | ||
747 | } | ||
748 | } | 747 | } |
748 | } | ||
749 | 749 | ||
750 | if (0 != strcmp(argv[5], "-")) | 750 | if (0 != strcmp (argv[5], "-")) |
751 | { | ||
751 | { | 752 | { |
752 | { | 753 | const char *address = argv[5]; |
753 | const char *address = argv[5]; | 754 | const char *mask = argv[6]; |
754 | const char *mask = argv[6]; | ||
755 | 755 | ||
756 | set_address4(dev, address, mask); | 756 | set_address4 (dev, address, mask); |
757 | } | ||
758 | if (0 != strcmp (argv[2], "-")) | ||
759 | { | ||
760 | { | ||
761 | char *const sysctl_args[] = { | ||
762 | "sysctl", "-w", "net.ipv4.ip_forward=1", NULL | ||
763 | }; | ||
764 | if (0 != fork_and_exec (sbin_sysctl, | ||
765 | sysctl_args)) | ||
766 | { | ||
767 | fprintf (stderr, | ||
768 | "Failed to enable IPv4 forwarding. Will continue anyway.\n"); | ||
769 | } | ||
757 | } | 770 | } |
758 | if (0 != strcmp(argv[2], "-")) | 771 | { |
772 | char *const iptables_args[] = { | ||
773 | "iptables", "-t", "nat", "-A", "POSTROUTING", "-o", argv[2], "-j", | ||
774 | "MASQUERADE", NULL | ||
775 | }; | ||
776 | if (0 != fork_and_exec (sbin_iptables, | ||
777 | iptables_args)) | ||
759 | { | 778 | { |
760 | { | 779 | fprintf (stderr, |
761 | char *const sysctl_args[] = | 780 | "Failed to enable IPv4 masquerading (NAT). Will continue anyway.\n"); |
762 | { | ||
763 | "sysctl", "-w", "net.ipv4.ip_forward=1", NULL | ||
764 | }; | ||
765 | if (0 != fork_and_exec(sbin_sysctl, | ||
766 | sysctl_args)) | ||
767 | { | ||
768 | fprintf(stderr, | ||
769 | "Failed to enable IPv4 forwarding. Will continue anyway.\n"); | ||
770 | } | ||
771 | } | ||
772 | { | ||
773 | char *const iptables_args[] = | ||
774 | { | ||
775 | "iptables", "-t", "nat", "-A", "POSTROUTING", "-o", argv[2], "-j", "MASQUERADE", NULL | ||
776 | }; | ||
777 | if (0 != fork_and_exec(sbin_iptables, | ||
778 | iptables_args)) | ||
779 | { | ||
780 | fprintf(stderr, | ||
781 | "Failed to enable IPv4 masquerading (NAT). Will continue anyway.\n"); | ||
782 | } | ||
783 | } | ||
784 | } | 781 | } |
782 | } | ||
785 | } | 783 | } |
784 | } | ||
786 | 785 | ||
787 | uid_t uid = getuid(); | 786 | uid_t uid = getuid (); |
788 | #ifdef HAVE_SETRESUID | 787 | #ifdef HAVE_SETRESUID |
789 | if (0 != setresuid(uid, uid, uid)) | 788 | if (0 != setresuid (uid, uid, uid)) |
790 | { | 789 | { |
791 | fprintf(stderr, "Failed to setresuid: %s\n", strerror(errno)); | 790 | fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno)); |
792 | global_ret = 2; | 791 | global_ret = 2; |
793 | goto cleanup; | 792 | goto cleanup; |
794 | } | 793 | } |
795 | #else | 794 | #else |
796 | if (0 != (setuid(uid) | seteuid(uid))) | 795 | if (0 != (setuid (uid) | seteuid (uid))) |
797 | { | 796 | { |
798 | fprintf(stderr, "Failed to setuid: %s\n", strerror(errno)); | 797 | fprintf (stderr, "Failed to setuid: %s\n", strerror (errno)); |
799 | global_ret = 2; | 798 | global_ret = 2; |
800 | goto cleanup; | 799 | goto cleanup; |
801 | } | 800 | } |
802 | #endif | 801 | #endif |
803 | 802 | ||
804 | if (SIG_ERR == signal(SIGPIPE, SIG_IGN)) | 803 | if (SIG_ERR == signal (SIGPIPE, SIG_IGN)) |
805 | { | 804 | { |
806 | fprintf(stderr, "Failed to protect against SIGPIPE: %s\n", | 805 | fprintf (stderr, "Failed to protect against SIGPIPE: %s\n", |
807 | strerror(errno)); | 806 | strerror (errno)); |
808 | /* no exit, we might as well die with SIGPIPE should it ever happen */ | 807 | /* no exit, we might as well die with SIGPIPE should it ever happen */ |
809 | } | 808 | } |
810 | run(fd_tun); | 809 | run (fd_tun); |
811 | global_ret = 0; | 810 | global_ret = 0; |
812 | cleanup: | 811 | cleanup: |
813 | (void)close(fd_tun); | 812 | (void) close (fd_tun); |
814 | return global_ret; | 813 | return global_ret; |
815 | } | 814 | } |
816 | 815 | ||