1 /*
2 * icmp-socket.c - ICMP socket implementations
3 *
4 * Copyright (C) 2011-2013 Thien-Thi Nguyen
5 * Copyright (C) 2000, 2001, 2003 Stefan Jahn <stefan@lkcc.org>
6 *
7 * This is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3, or (at your option)
10 * any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this package. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include "timidity.h"
24 #include <stdio.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <time.h>
31
32 #if HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35
36 #ifndef __MINGW32__
37 # include <sys/types.h>
38 # include <sys/socket.h>
39 #endif
40
41 #ifdef __MINGW32__
42 # include <process.h>
43 #endif
44 #include "networking-headers.h"
45 #include "libserveez/util.h"
46 #include "libserveez/socket.h"
47 #include "libserveez/core.h"
48 #include "libserveez/server-core.h"
49 #include "libserveez/icmp-socket.h"
50 #include "libserveez/raw-socket.h"
51 #include "libserveez/server.h"
52 #include "libserveez/binding.h"
53
54 #define IP_HEADER_SIZE 20
55
56 /* general definitions */
57 #define ICMP_ECHOREPLY 0 /* Echo Reply */
58 #define ICMP_DEST_UNREACH 3 /* Destination Unreachable */
59 #define ICMP_SOURCE_QUENCH 4 /* Source Quench */
60 #define ICMP_REDIRECT 5 /* Redirect (change route) */
61 #define ICMP_ECHO 8 /* Echo Request */
62 #define ICMP_TIME_EXCEEDED 11 /* Time Exceeded */
63 #define ICMP_PARAMETERPROB 12 /* Parameter Problem */
64 #define ICMP_TIMESTAMP 13 /* Timestamp Request */
65 #define ICMP_TIMESTAMPREPLY 14 /* Timestamp Reply */
66 #define ICMP_INFO_REQUEST 15 /* Information Request */
67 #define ICMP_INFO_REPLY 16 /* Information Reply */
68 #define ICMP_ADDRESS 17 /* Address Mask Request */
69 #define ICMP_ADDRESSREPLY 18 /* Address Mask Reply */
70 #define ICMP_MAX_TYPE 18
71
72 /* Text representation of ICMP type codes. */
73 static char *icmp_request[] = {
74 "echo reply",
75 NULL,
76 NULL,
77 "destination unreachable",
78 "source quench",
79 "redirect (change route)",
80 NULL,
81 NULL,
82 "echo request",
83 NULL,
84 NULL,
85 "time exceeded",
86 "parameter problem",
87 "timestamp request",
88 "timestamp reply",
89 "information request",
90 "information reply",
91 "address mask request",
92 "address mask reply"
93 };
94
95 /* ICMP header structure. */
96 typedef struct
97 {
98 uint8_t type; /* message type */
99 uint8_t code; /* type sub-code */
100 uint16_t checksum; /* check sum */
101 uint16_t ident; /* identifier */
102 uint16_t sequence; /* sequence number */
103 in_port_t port; /* remote port address */
104 }
105 svz_icmp_header_t;
106
107 #ifdef __MINGW32__
108
109 /*
110 * Microsoft discourages the use of their ICMP.DLL API, but it seems
111 * to be the only way to make use of raw sockets anyway. The API is
112 * almostly unusable because:
113 * 1. you cannot receive if not previously sent a packet
114 * 2. the IcmpSendEcho call is blocking
115 * 3. receive and send is one call
116 * 4. you cannot set the ICMP header (type, code)
117 */
118
119 /*
120 * Note 2: For the most part, you can refer to RFC 791 for details
121 * on how to fill in values for the IP option information structure.
122 */
123 typedef struct ip_option_information
124 {
125 uint8_t Ttl; /* Time To Live (used for traceroute) */
126 uint8_t Tos; /* Type Of Service (usually 0) */
127 uint8_t Flags; /* IP header flags (usually 0) */
128 uint8_t OptionsSize; /* Size of options data (usually 0, max 40) */
129 uint8_t *OptionsData; /* Options data buffer */
130 }
131 IPINFO;
132
133 /*
134 * Note 1: The Reply Buffer will have an array of ICMP_ECHO_REPLY
135 * structures, followed by options and the data in ICMP echo reply
136 * datagram received. You must have room for at least one ICMP
137 * echo reply structure, plus 8 bytes for an ICMP header.
138 */
139 typedef struct icmp_echo_reply
140 {
141 in_addr_t Address; /* source address */
142 unsigned long Status; /* IP status value (see below) */
143 unsigned long RTTime; /* Round Trip Time in milliseconds */
144 uint16_t DataSize; /* reply data size */
145 uint16_t Reserved;
146 void *Data; /* reply data buffer */
147 IPINFO Options; /* reply options */
148 }
149 ICMPECHO;
150
151 /*
152 * DLL function definitions of IcmpCloseHandle, IcmpCreateFile,
153 * IcmpParseReplies, IcmpSendEcho and IcmpSendEcho2.
154 */
155 typedef HANDLE (__stdcall * IcmpCreateFileProc) (void);
156 typedef BOOL (__stdcall * IcmpCloseHandleProc) (HANDLE IcmpHandle);
157 typedef DWORD (__stdcall * IcmpSendEchoProc) (
158 HANDLE IcmpHandle, /* handle returned from ‘IcmpCreateFile’ */
159 in_addr_t DestAddress, /* destination IP address (in network order) */
160 void *RequestData, /* pointer to buffer to send */
161 uint16_t RequestSize, /* length of data in buffer */
162 IPINFO *RequestOptns, /* see Note 2 */
163 void *ReplyBuffer, /* see Note 1 */
164 unsigned long ReplySize, /* length of reply (at least 1 reply) */
165 unsigned long Timeout /* time in milliseconds to wait for reply */
166 );
167
168 /*
169 * Error definitions.
170 */
171 #define IP_STATUS_BASE 11000
172 #define IP_SUCCESS 0
173 #define IP_BUF_TOO_SMALL (IP_STATUS_BASE + 1)
174 #define IP_DEST_NET_UNREACHABLE (IP_STATUS_BASE + 2)
175 #define IP_DEST_HOST_UNREACHABLE (IP_STATUS_BASE + 3)
176 #define IP_DEST_PROT_UNREACHABLE (IP_STATUS_BASE + 4)
177 #define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5)
178 #define IP_NO_RESOURCES (IP_STATUS_BASE + 6)
179 #define IP_BAD_OPTION (IP_STATUS_BASE + 7)
180 #define IP_HW_ERROR (IP_STATUS_BASE + 8)
181 #define IP_PACKET_TOO_BIG (IP_STATUS_BASE + 9)
182 #define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10)
183 #define IP_BAD_REQ (IP_STATUS_BASE + 11)
184 #define IP_BAD_ROUTE (IP_STATUS_BASE + 12)
185 #define IP_TTL_EXPIRED_TRANSIT (IP_STATUS_BASE + 13)
186 #define IP_TTL_EXPIRED_REASSEM (IP_STATUS_BASE + 14)
187 #define IP_PARAM_PROBLEM (IP_STATUS_BASE + 15)
188 #define IP_SOURCE_QUENCH (IP_STATUS_BASE + 16)
189 #define IP_OPTION_TOO_BIG (IP_STATUS_BASE + 17)
190 #define IP_BAD_DESTINATION (IP_STATUS_BASE + 18)
191 #define IP_ADDR_DELETED (IP_STATUS_BASE + 19)
192 #define IP_SPEC_MTU_CHANGE (IP_STATUS_BASE + 20)
193 #define IP_MTU_CHANGE (IP_STATUS_BASE + 21)
194 #define IP_UNLOAD (IP_STATUS_BASE + 22)
195 #define IP_GENERAL_FAILURE (IP_STATUS_BASE + 50)
196 #define MAX_IP_STATUS IP_GENERAL_FAILURE
197 #define IP_PENDING (IP_STATUS_BASE + 255)
198
199 /* Functions and handles for the ICMP.DLL API. */
200 static IcmpCreateFileProc IcmpCreateFile = NULL;
201 static IcmpCloseHandleProc IcmpCloseHandle = NULL;
202 static IcmpSendEchoProc IcmpSendEcho = NULL;
203 static HANDLE IcmpHandle = NULL;
204 static HANDLE hIcmp = INVALID_HANDLE_VALUE;
205
206 /*
207 * Load the @file{ICMP.DLL} library into process address space and get all
208 * necessary function pointers.
209 */
210 void
svz_icmp_startup(void)211 svz_icmp_startup (void)
212 {
213 /* load library */
214 if ((IcmpHandle = LoadLibrary ("ICMP.DLL")) == NULL)
215 {
216 svz_log_sys_error ("icmp: LoadLibrary");
217 return;
218 }
219
220 /* obtain functions */
221 IcmpCreateFile = (IcmpCreateFileProc)
222 GetProcAddress (IcmpHandle, "IcmpCreateFile");
223 IcmpCloseHandle = (IcmpCloseHandleProc)
224 GetProcAddress (IcmpHandle, "IcmpCloseHandle");
225 IcmpSendEcho = (IcmpSendEchoProc)
226 GetProcAddress (IcmpHandle, "IcmpSendEcho");
227 if (IcmpSendEcho == NULL ||
228 IcmpCloseHandle == NULL || IcmpCreateFile == NULL)
229 {
230 svz_log_sys_error ("icmp: GetProcAddress");
231 FreeLibrary (IcmpHandle);
232 IcmpHandle = NULL;
233 return;
234 }
235
236 /* open ping service */
237 if (svz_invalid_handle_p (hIcmp = IcmpCreateFile ()))
238 {
239 svz_log_sys_error ("IcmpCreateFile");
240 FreeLibrary (IcmpHandle);
241 IcmpHandle = NULL;
242 return;
243 }
244
245 #if ENABLE_DEBUG
246 svz_log (SVZ_LOG_DEBUG, "icmp services successfully initialized\n");
247 #endif
248 }
249
250 /*
251 * Shutdown the ping service.
252 */
253 void
svz_icmp_cleanup(void)254 svz_icmp_cleanup (void)
255 {
256 /* close ip service */
257 if (! svz_invalid_handle_p (hIcmp))
258 {
259 if (!IcmpCloseHandle (hIcmp))
260 svz_log_sys_error ("IcmpCloseHandle");
261 }
262
263 /* release ICMP.DLL */
264 if (IcmpHandle)
265 {
266 FreeLibrary (IcmpHandle);
267 IcmpHandle = NULL;
268 }
269 }
270 #endif /* __MINGW32__ */
271
272 /* Static buffer for ip packets. */
273 static char icmp_buffer[IP_HEADER_SIZE + ICMP_HEADER_SIZE + ICMP_MSG_SIZE];
274
275 /*
276 * Get ICMP header from plain data.
277 */
278 static svz_icmp_header_t *
unpack_header(uint8_t * data)279 unpack_header (uint8_t *data)
280 {
281 static svz_icmp_header_t hdr;
282 uint16_t uint16;
283
284 hdr.type = *data++;
285 hdr.code = *data++;
286 memcpy (&uint16, data, SIZEOF_UINT16);
287 hdr.checksum = ntohs (uint16);
288 data += SIZEOF_UINT16;
289 memcpy (&uint16, data, SIZEOF_UINT16);
290 hdr.ident = ntohs (uint16);
291 data += SIZEOF_UINT16;
292 memcpy (&uint16, data, SIZEOF_UINT16);
293 hdr.sequence = ntohs (uint16);
294 data += SIZEOF_UINT16;
295 memcpy (&uint16, data, SIZEOF_UINT16);
296 hdr.port = uint16;
297
298 return &hdr;
299 }
300
301 /*
302 * Create ICMP header (data block) from given structure.
303 */
304 static uint8_t *
pack_header(svz_icmp_header_t * hdr)305 pack_header (svz_icmp_header_t *hdr)
306 {
307 static uint8_t buffer[ICMP_HEADER_SIZE];
308 uint8_t *data = buffer;
309 uint16_t uint16;
310
311 *data++ = hdr->type;
312 *data++ = hdr->code;
313 uint16 = htons (hdr->checksum);
314 memcpy (data, &uint16, SIZEOF_UINT16);
315 data += SIZEOF_UINT16;
316 uint16 = htons (hdr->ident);
317 memcpy (data, &uint16, SIZEOF_UINT16);
318 data += SIZEOF_UINT16;
319 uint16 = htons (hdr->sequence);
320 memcpy (data, &uint16, SIZEOF_UINT16);
321 data += SIZEOF_UINT16;
322 uint16 = hdr->port;
323 memcpy (data, &uint16, SIZEOF_UINT16);
324
325 return buffer;
326 }
327
328 #define ICMP_ERROR -1
329 #define ICMP_DISCONNECT -2
330
331 /*
332 * Parse and check IP and ICMP header. Return the amount of leading bytes
333 * to be truncated. Return ICMP_ERROR on packet errors and return
334 * ICMP_DISCONNECT when we received an disconnection signal.
335 */
336 static int
check_packet(svz_socket_t * sock,uint8_t * data,int len)337 check_packet (svz_socket_t *sock, uint8_t *data, int len)
338 {
339 int length;
340 uint8_t *p = data;
341 svz_icmp_header_t *header;
342
343 /* First check the IP header. */
344 if ((length = svz_raw_check_ip_header (p, len)) == -1)
345 return ICMP_ERROR;
346
347 /* Get the actual ICMP header. */
348 header = unpack_header (p + length);
349 p += length + ICMP_HEADER_SIZE;
350 len -= length + ICMP_HEADER_SIZE;
351
352 /* Do these checks only if it is the right kind of packet. */
353 if (header->type == sock->itype)
354 {
355 /* validate the ICMP data checksum */
356 if (header->checksum != svz_raw_ip_checksum (p, len))
357 {
358 #if ENABLE_DEBUG
359 svz_log (SVZ_LOG_DEBUG, "icmp: invalid data checksum\n");
360 #endif
361 return ICMP_ERROR;
362 }
363
364 /* check the ICMP header identification */
365 if (header->ident == getpid () + sock->id)
366 {
367 #if ENABLE_DEBUG
368 svz_log (SVZ_LOG_DEBUG, "icmp: rejecting native packet\n");
369 #endif
370 return ICMP_ERROR;
371 }
372
373 /* check ICMP remote port */
374 if ((header->port != sock->remote_port) &&
375 !(sock->flags & SVZ_SOFLG_LISTENING))
376 {
377 #if ENABLE_DEBUG
378 svz_log (SVZ_LOG_DEBUG, "icmp: rejecting filtered packet\n");
379 #endif
380 return ICMP_ERROR;
381 }
382 sock->remote_port = header->port;
383 }
384
385 /* What kind of packet is this ? */
386 #if ENABLE_DEBUG
387 else if (header->type <= ICMP_MAX_TYPE)
388 {
389 if (icmp_request[header->type])
390 svz_log (SVZ_LOG_DEBUG, "icmp: %s received\n",
391 icmp_request[header->type]);
392 else
393 svz_log (SVZ_LOG_DEBUG, "unsupported protocol 0x%02X received\n",
394 header->type);
395 return ICMP_ERROR;
396 }
397 #endif /* ENABLE_DEBUG */
398
399 if (header->type == sock->itype)
400 {
401 if (header->code == SVZ_ICMP_SERVEEZ_CONNECT &&
402 sock->flags & SVZ_SOFLG_LISTENING)
403 {
404 svz_log (SVZ_LOG_NOTICE, "icmp: accepting connection\n");
405 }
406 else if (header->code == SVZ_ICMP_SERVEEZ_CLOSE)
407 {
408 svz_log (SVZ_LOG_NOTICE, "icmp: closing connection\n");
409 return ICMP_DISCONNECT;
410 }
411 return (length + ICMP_HEADER_SIZE);
412 }
413 #if ENABLE_DEBUG
414 else
415 {
416 svz_log (SVZ_LOG_DEBUG, "unsupported protocol 0x%02X received\n",
417 header->type);
418 }
419 #endif /* ENABLE_DEBUG */
420
421 return ICMP_ERROR;
422 }
423
424 /*
425 * Default reader for ICMP sockets. The sender is stored within
426 * @code{sock->remote_addr} and @code{sock->remote_port} afterwards.
427 */
428 static int
read_socket(svz_socket_t * sock)429 read_socket (svz_socket_t *sock)
430 {
431 int num_read;
432 socklen_t len;
433 struct sockaddr_in sender;
434 int trunc;
435
436 len = sizeof (struct sockaddr_in);
437
438 /* Receive data. */
439 if (!(sock->flags & SVZ_SOFLG_CONNECTED))
440 {
441 num_read = recvfrom (sock->sock_desc, icmp_buffer,
442 sizeof (icmp_buffer), 0,
443 (struct sockaddr *) &sender, &len);
444 }
445 else
446 {
447 num_read = recv (sock->sock_desc, icmp_buffer,
448 sizeof (icmp_buffer), 0);
449 }
450
451 /* Valid packet data arrived. */
452 if (num_read > 0)
453 {
454 #if 0
455 svz_hexdump (stdout, "icmp packet received", sock->sock_desc,
456 icmp_buffer, num_read, 0);
457 #endif
458 sock->last_recv = time (NULL);
459 if (!(sock->flags & SVZ_SOFLG_FIXED))
460 SVZ_SET_ADDR (sock->remote_addr, AF_INET, &sender.sin_addr.s_addr);
461 #if ENABLE_DEBUG
462 {
463 char buf[64];
464
465 svz_log (SVZ_LOG_DEBUG, "icmp: recv%s: %s (%u bytes)\n",
466 sock->flags & SVZ_SOFLG_CONNECTED ? "" : "from",
467 SVZ_PP_ADDR (buf, sock->remote_addr), num_read);
468 }
469 #endif /* ENABLE_DEBUG */
470
471 /*
472 * Check the ICMP packet and put the packet load only into the
473 * receive buffer of the socket structure.
474 */
475 trunc = check_packet (sock, (uint8_t *) icmp_buffer, num_read);
476 if (trunc >= 0)
477 {
478 num_read -= trunc;
479 if (num_read > sock->recv_buffer_size - sock->recv_buffer_fill)
480 {
481 svz_log (SVZ_LOG_ERROR,
482 "receive buffer overflow on icmp socket %d\n",
483 sock->sock_desc);
484 return -1;
485 }
486
487 memcpy (sock->recv_buffer + sock->recv_buffer_fill,
488 icmp_buffer + trunc, num_read);
489 sock->recv_buffer_fill += num_read;
490
491 /* Check access lists. */
492 if (svz_sock_check_access (sock, sock) < 0)
493 return 0;
494
495 if (sock->check_request)
496 sock->check_request (sock);
497 }
498 else if (trunc == ICMP_DISCONNECT)
499 {
500 return -1;
501 }
502 }
503 /* Some error occurred. */
504 else
505 {
506 svz_log_net_error ("icmp: recv%s", (sock->flags & SVZ_SOFLG_CONNECTED
507 ? ""
508 : "from"));
509 if (! svz_socket_unavailable_error_p ())
510 return -1;
511 }
512 return 0;
513 }
514
515 /*
516 * Default reader for ICMP server sockets. Allocates necessary buffers and
517 * reverts to @code{read_socket}.
518 */
519 int
svz_icmp_lazy_read_socket(svz_socket_t * sock)520 svz_icmp_lazy_read_socket (svz_socket_t *sock)
521 {
522 svz_portcfg_t *port = sock->port;
523
524 svz_sock_resize_buffers (sock, port->send_buffer_size,
525 port->recv_buffer_size);
526 sock->read_socket = read_socket;
527
528 return sock->read_socket (sock);
529 }
530
531 /*
532 * The default ICMP write callback is called whenever the socket
533 * descriptor has been @code{select}'ed or @code{poll}'ed to be ready for
534 * sending.
535 */
536 int
svz_icmp_write_socket(svz_socket_t * sock)537 svz_icmp_write_socket (svz_socket_t *sock)
538 {
539 int num_written;
540 unsigned do_write;
541 char *p;
542 socklen_t len;
543 struct sockaddr_in receiver;
544
545 /* Return here if there is nothing to do. */
546 if (sock->send_buffer_fill <= 0)
547 return 0;
548
549 len = sizeof (struct sockaddr_in);
550 receiver.sin_family = AF_INET;
551
552 /* Get destination address and data length from send buffer. */
553 p = sock->send_buffer;
554 memcpy (&do_write, p, sizeof (do_write));
555 p += sizeof (do_write);
556 memcpy (&receiver.sin_addr.s_addr, p, sizeof (in_addr_t));
557 p += sizeof (in_addr_t);
558 memcpy (&receiver.sin_port, p, sizeof (sock->remote_port));
559 p += sizeof (sock->remote_port);
560 assert ((int) do_write <= sock->send_buffer_fill);
561
562 /* If socket is `connect ()'ed use `send ()' instead of `sendto ()'. */
563 if (!(sock->flags & SVZ_SOFLG_CONNECTED))
564 {
565 num_written = sendto (sock->sock_desc, p,
566 do_write - (p - sock->send_buffer),
567 0, (struct sockaddr *) &receiver, len);
568 }
569 else
570 {
571 num_written = send (sock->sock_desc, p,
572 do_write - (p - sock->send_buffer), 0);
573 }
574
575 /* Some error occurred while sending. */
576 if (num_written < 0)
577 {
578 svz_log_net_error ("icmp: send%s", (sock->flags & SVZ_SOFLG_CONNECTED
579 ? ""
580 : "to"));
581 if (svz_socket_unavailable_error_p ())
582 num_written = 0;
583 }
584 /* Packet data could be transmitted. */
585 else
586 {
587 sock->last_send = time (NULL);
588 if ((unsigned) sock->send_buffer_fill > do_write)
589 {
590 memmove (sock->send_buffer,
591 sock->send_buffer + do_write,
592 sock->send_buffer_fill - do_write);
593 }
594 sock->send_buffer_fill -= do_write;
595 }
596
597 #if ENABLE_DEBUG
598 svz_log (SVZ_LOG_DEBUG, "icmp: send%s: %s (%u bytes)\n",
599 sock->flags & SVZ_SOFLG_CONNECTED ? "" : "to",
600 svz_inet_ntoa (receiver.sin_addr.s_addr),
601 do_write - (p - sock->send_buffer));
602 #endif /* ENABLE_DEBUG */
603
604 return num_written < 0 ? -1 : 0;
605 }
606
607 /**
608 * ``If you are calling this function we will send an empty ICMP packet
609 * signaling that this connection is going down soon.''
610 * [ttn sez: huh?]
611 */
612 int
svz_icmp_send_control(svz_socket_t * sock,uint8_t type)613 svz_icmp_send_control (svz_socket_t *sock, uint8_t type)
614 {
615 static char *buffer = icmp_buffer;
616 svz_icmp_header_t hdr;
617 unsigned len;
618 int ret = 0;
619
620 len = sizeof (len);
621 svz_address_to (&buffer[len], sock->remote_addr);
622 len += sizeof (in_addr_t);
623 memcpy (&buffer[len], &sock->remote_port, sizeof (sock->remote_port));
624 len += sizeof (sock->remote_port);
625
626 hdr.type = sock->itype;
627 hdr.code = type;
628 hdr.checksum = svz_raw_ip_checksum (NULL, 0);
629 hdr.ident = (uint16_t) (getpid () + sock->id);
630 hdr.sequence = sock->send_seq;
631 hdr.port = sock->remote_port;
632 memcpy (&buffer[len], pack_header (&hdr), ICMP_HEADER_SIZE);
633 len += ICMP_HEADER_SIZE;
634 memcpy (buffer, &len, sizeof (len));
635
636 if ((ret = svz_sock_write (sock, buffer, len)) == -1)
637 {
638 sock->flags |= SVZ_SOFLG_KILLED;
639 }
640 return ret;
641 }
642
643 /**
644 * Send @var{buf} with length @var{length} via this ICMP socket @var{sock}.
645 * If @var{length} supersedes the maximum ICMP message size the buffer is
646 * split into smaller packets.
647 */
648 int
svz_icmp_write(svz_socket_t * sock,char * buf,int length)649 svz_icmp_write (svz_socket_t *sock, char *buf, int length)
650 {
651 static char *buffer = icmp_buffer;
652 svz_icmp_header_t hdr;
653 unsigned len, size;
654 int ret = 0;
655
656 /* Return if the socket has already been killed. */
657 if (sock->flags & SVZ_SOFLG_KILLED)
658 return 0;
659
660 while (length)
661 {
662 /*
663 * Put the data length and destination address in front
664 * of each packet.
665 */
666 len = sizeof (len);
667 svz_address_to (&buffer[len], sock->remote_addr);
668 len += sizeof (in_addr_t);
669 memcpy (&buffer[len], &sock->remote_port, sizeof (sock->remote_port));
670 len += sizeof (sock->remote_port);
671 if ((size = length) > ICMP_MSG_SIZE)
672 size = ICMP_MSG_SIZE;
673
674 /* Create ICMP header and put it in front of packet load. */
675 hdr.type = sock->itype;
676 hdr.code = SVZ_ICMP_SERVEEZ_DATA;
677 hdr.checksum = svz_raw_ip_checksum ((uint8_t *) buf, size);
678 hdr.ident = (uint16_t) (getpid () + sock->id);
679 hdr.sequence = sock->send_seq++;
680 hdr.port = sock->remote_port;
681 memcpy (&buffer[len], pack_header (&hdr), ICMP_HEADER_SIZE);
682 len += ICMP_HEADER_SIZE;
683
684 /* Copy the given buffer. */
685 memcpy (&buffer[len], buf, size);
686 len += size;
687
688 /* Put chunk length to buffer. */
689 memcpy (buffer, &len, sizeof (len));
690 buf += size;
691 length -= size;
692
693 /* Actually send the data or put it into the send buffer queue. */
694 if ((ret = svz_sock_write (sock, buffer, len)) == -1)
695 {
696 sock->flags |= SVZ_SOFLG_KILLED;
697 break;
698 }
699 }
700
701 return ret;
702 }
703
704 /*
705 * Default @code{check_request} callback for ICMP sockets.
706 */
707 int
svz_icmp_check_request(svz_socket_t * sock)708 svz_icmp_check_request (svz_socket_t *sock)
709 {
710 size_t n;
711 svz_server_t *server;
712 svz_array_t *bindings;
713 svz_binding_t *binding;
714
715 if (svz_sock_bindings (sock) == NULL && sock->handle_request == NULL)
716 return -1;
717
718 /*
719 * If there is a valid `handle_request' callback (dedicated icmp
720 * connection) call it. This kind of behaviour is due to a socket
721 * creation via 'icmp_connect' (s.b.) and setting up a static
722 * `handle_request' callback.
723 */
724 if (sock->handle_request)
725 {
726 if (sock->handle_request (sock, sock->recv_buffer,
727 sock->recv_buffer_fill))
728 return -1;
729 sock->recv_buffer_fill = 0;
730 return 0;
731 }
732
733 /* Go through all icmp servers on this server socket. */
734 bindings = svz_binding_filter (sock);
735 svz_array_foreach (bindings, binding, n)
736 {
737 server = binding->server;
738 sock->cfg = server->cfg;
739
740 if (server->handle_request)
741 {
742 if (!server->handle_request (sock, sock->recv_buffer,
743 sock->recv_buffer_fill))
744 {
745 sock->recv_buffer_fill = 0;
746 break;
747 }
748 }
749 }
750 svz_array_destroy (bindings);
751
752 /* Check if any server processed this packet. */
753 if (sock->recv_buffer_fill)
754 {
755 #if ENABLE_DEBUG
756 svz_log (SVZ_LOG_DEBUG, "rejecting icmp packet on socket %d\n",
757 sock->sock_desc);
758 #endif
759 sock->recv_buffer_fill = 0;
760 }
761
762 sock->cfg = NULL;
763 return 0;
764 }
765
766 /**
767 * Create an ICMP socket for receiving and sending.
768 * Return @code{NULL} on errors, otherwise an enqueued socket structure.
769 */
770 svz_socket_t *
svz_icmp_connect(svz_address_t * host,in_port_t port,uint8_t type)771 svz_icmp_connect (svz_address_t *host, in_port_t port, uint8_t type)
772 {
773 svz_t_socket sockfd;
774 svz_socket_t *sock;
775
776 STILL_NO_V6_DAMMIT (host);
777
778 /* Create a client socket. */
779 if ((sockfd = svz_socket_create (SVZ_PROTO_ICMP)) == (svz_t_socket) -1)
780 return NULL;
781
782 /* Try to connect to the server. Does it make sense for ICMP ? */
783 if (svz_socket_connect (sockfd, host, port) == -1)
784 return NULL;
785
786 /* Create socket structure and enqueue it. */
787 if ((sock = svz_sock_alloc ()) == NULL)
788 {
789 svz_closesocket (sockfd);
790 return NULL;
791 }
792
793 svz_sock_resize_buffers (sock, ICMP_BUF_SIZE, ICMP_BUF_SIZE);
794 svz_sock_unique_id (sock);
795 sock->sock_desc = sockfd;
796 sock->proto = SVZ_PROTO_ICMP;
797 sock->flags |= (SVZ_SOFLG_SOCK | SVZ_SOFLG_CONNECTED | SVZ_SOFLG_FIXED);
798 sock->itype = type;
799 svz_sock_enqueue (sock);
800 svz_sock_intern_connection_info (sock);
801
802 /* Put foreign address here. */
803 sock->remote_addr = svz_address_copy (host);
804 sock->remote_port = sock->id;
805
806 sock->read_socket = read_socket;
807 sock->write_socket = svz_icmp_write_socket;
808 sock->check_request = svz_icmp_check_request;
809
810 /* Finally send a connection message. */
811 svz_icmp_send_control (sock, SVZ_ICMP_SERVEEZ_CONNECT);
812 svz_sock_connections++;
813 return sock;
814 }
815