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