]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ICMPSquid.cc
Merged from trunk.
[thirdparty/squid.git] / src / ICMPSquid.cc
CommitLineData
cc192b50 1/*
2 * $Id: ICMPSquid.cc,v 1.1 2007/12/14 23:11:45 amosjeffries Exp $
3 *
4 * DEBUG: section 37 ICMP Routines
5 * AUTHOR: Duane Wessels, Amos Jeffries
6 *
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 *
33 */
34
35#include "squid.h"
36#include "ICMPSquid.h"
37#include "comm.h"
38#include "SquidTime.h"
39
40// Instance global to be available in main() and elsewhere.
41ICMPSquid icmpEngine;
42
43#if USE_ICMP
44
45#define S_ICMP_ECHO 1
46#if DEAD_CODE
47#define S_ICMP_ICP 2
48#endif
49#define S_ICMP_DOM 3
50
51static void * hIpc;
52static pid_t pid;
53
54#endif /* USE_ICMP */
55
56
57ICMPSquid::ICMPSquid() : ICMP()
58{
59 ; // nothing new.
60}
61
62ICMPSquid::~ICMPSquid()
63{
64 Close();
65}
66
67
68#if USE_ICMP
69
70void
71ICMPSquid::SendEcho(IPAddress &to, int opcode, const char *payload, int len)
72{
73 static pingerEchoData pecho;
74 int x, slen;
75
76 /** \li Does nothing if the pinger socket is not available. */
77 if(icmp_sock < 0) {
78 debugs(37, 2, HERE << " Socket Closed. Aborted send to " << pecho.to << ", opcode " << opcode << ", len " << pecho.psize);
79 return;
80 }
81
82 /** \li If no payload is given or is set as NULL it will ignore payload and len */
83 if(!payload)
84 len = 0;
85
86 /** \li Otherwise if len is 0, uses strlen() to detect length of payload.
87 \bug This will result in part of the payload being truncated if it contains a NULL character.
88 \bug Or it may result in a buffer over-run if the payload is not nul-terminated properly.
89 */
90 else if (payload && len == 0)
91 len = strlen(payload);
92
93 /** \li
94 \bug If length specified or auto-detected is greater than the possible payload squid will die with an assert.
95 \todo This should perhapse be reduced to a truncated payload? or no payload. A WARNING is due anyway.
96 */
97 assert(len <= PINGER_PAYLOAD_SZ);
98
99 pecho.to = to;
100
101 pecho.opcode = (unsigned char) opcode;
102
103 pecho.psize = len;
104
105 if(len > 0)
106 xmemcpy(pecho.payload, payload, len);
107
108 slen = sizeof(pingerEchoData) - PINGER_PAYLOAD_SZ + pecho.psize;
109
110 debugs(37, 2, HERE << "to " << pecho.to << ", opcode " << opcode << ", len " << pecho.psize);
111
112 x = comm_udp_send(icmp_sock, (char *)&pecho, slen, 0);
113
114 if (x < 0) {
115 debugs(37, 1, HERE << "send: " << xstrerror());
116
117 /** \li If the send results in ECONNREFUSED or EPIPE errors from helper, will cleanly shutdown the module. */
118 /** \todo This should try restarting the helper a few times?? before giving up? */
119 if (errno == ECONNREFUSED || errno == EPIPE) {
120 Close();
121 return;
122 }
123 /** All other send errors are ignored. */
9166e142 124 } else if (x != slen) {
cc192b50 125 debugs(37, 1, HERE << "Wrote " << x << " of " << slen << " bytes");
126 }
127}
128
129// static Callback to wrap the squid-side ICMP handler.
130// the ICMPSquid::Recv cannot be declared both static and virtual.
131static void
132icmpSquidRecv(int unused1, void *unused2)
133{
134 icmpEngine.Recv();
135}
136
137void
138ICMPSquid::Recv()
139{
140 int n;
141 static int fail_count = 0;
142 pingerReplyData preply;
143 static IPAddress F;
144
145 commSetSelect(icmp_sock, COMM_SELECT_READ, icmpSquidRecv, NULL, 0);
146 memset(&preply, '\0', sizeof(pingerReplyData));
147 n = comm_udp_recv(icmp_sock,
148 (char *) &preply,
149 sizeof(pingerReplyData),
150 0);
151
152 if (n < 0 && EAGAIN != errno) {
153 debugs(37, 1, HERE << "recv: " << xstrerror());
154
155 if (errno == ECONNREFUSED)
156 Close();
157
158 if (errno == ECONNRESET)
159 Close();
160
161 if (++fail_count == 10)
162 Close();
163
164 return;
165 }
166
167 fail_count = 0;
168
169 /** If its a test probe from the pinger. Do nothing. */
170 if (n == 0) {
171 return;
172 }
173
174 F = preply.from;
175
176 F.SetPort(0);
177
178 switch (preply.opcode) {
179
180 case S_ICMP_ECHO:
181 debugs(37,4, HERE << " ICMP_ECHO of " << preply.from << " gave: hops=" << preply.hops <<", rtt=" << preply.rtt);
182 break;
183
184 case S_ICMP_DOM:
185 debugs(37,4, HERE << " DomainPing of " << preply.from << " gave: hops=" << preply.hops <<", rtt=" << preply.rtt);
186 netdbHandlePingReply(F, preply.hops, preply.rtt);
187 break;
188
189 default:
190 debugs(37, 1, HERE << "Bad opcode: " << preply.opcode << " from " << F);
191 break;
192 }
193}
194
195#endif /* USE_ICMP */
196
197void
198ICMPSquid::DomainPing(IPAddress &to, const char *domain)
199{
200#if USE_ICMP
201 debugs(37, 4, HERE << "'" << domain << "' (" << to << ")");
202 SendEcho(to, S_ICMP_DOM, domain, 0);
203#endif
204}
205
206int
207ICMPSquid::Open(void)
208{
209#if USE_ICMP
210 const char *args[2];
211 int rfd;
212 int wfd;
213 IPAddress localhost;
214
215 /* User configured disabled. */
216 if(!Config.pinger.enable) {
217 Close();
218 return -1;
219 }
220
221 args[0] = "(pinger)";
222 args[1] = NULL;
223 localhost.SetLocalhost();
224
225 /*
226 * Do NOT use IPC_DGRAM (=IPC_UNIX_DGRAM) here because you can't
227 * send() more than 4096 bytes on a socketpair() socket (at
228 * least on FreeBSD).
229 */
230 pid = ipcCreate(IPC_UDP_SOCKET,
231 Config.pinger.program,
232 args,
233 "Pinger Socket",
234 localhost,
235 &rfd,
236 &wfd,
237 &hIpc);
238
239 if (pid < 0)
240 return -1;
241
242 assert(rfd == wfd);
243
244 icmp_sock = rfd;
245
246 fd_note(icmp_sock, "pinger");
247
248 commSetSelect(icmp_sock, COMM_SELECT_READ, icmpSquidRecv, NULL, 0);
249
250 commSetTimeout(icmp_sock, -1, NULL, NULL);
251
252 debugs(37, 1, HERE << "Pinger socket opened on FD " << icmp_sock);
253
254 /* Tests the pinger immediately using localhost */
255#if USE_IPV6
256 SendEcho(localhost, S_ICMP_ECHO, "ip6-localhost");
257#endif
258 if(localhost.SetIPv4())
259 SendEcho(localhost, S_ICMP_ECHO, "localhost");
260
261#ifdef _SQUID_MSWIN_
262
263 debugs(37, 4, HERE << "Pinger handle: 0x" << std::hex << hIpc << std::dec << ", PID: " << pid);
264
265#endif /* _SQUID_MSWIN_ */
cc192b50 266 return icmp_sock;
b8e19d32
HN
267#else /* USE_ICMP */
268 return -1;
269#endif /* USE_ICMP */
cc192b50 270}
271
272void
273ICMPSquid::Close(void)
274{
275#if USE_ICMP
276
277 if (icmp_sock < 0)
278 return;
279
280 debugs(37, 1, HERE << "Closing Pinger socket on FD " << icmp_sock);
281
282#ifdef _SQUID_MSWIN_
283
284 send(icmp_sock, (const void *) "$shutdown\n", 10, 0);
285
286#endif
287
288 comm_close(icmp_sock);
289
290#ifdef _SQUID_MSWIN_
291
292 if (hIpc) {
293 if (WaitForSingleObject(hIpc, 12000) != WAIT_OBJECT_0) {
294 getCurrentTime();
295 debugs(37, 0, HERE << "WARNING: (pinger," << pid << ") didn't exit in 12 seconds");
296 }
297
298 CloseHandle(hIpc);
299 }
300
301#endif
302 icmp_sock = -1;
303
304#endif
305}