1 /*
2  * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 42    ICMP Pinger program */
10 
11 #define SQUID_HELPER 1
12 
13 #include "squid.h"
14 
15 #if USE_ICMP
16 
17 #include "Debug.h"
18 #include "Icmp4.h"
19 #include "Icmp6.h"
20 #include "IcmpPinger.h"
21 #include "SquidTime.h"
22 
23 #include <cerrno>
24 
IcmpPinger()25 IcmpPinger::IcmpPinger() : Icmp()
26 {
27     // these start invalid. Setup properly in Open()
28     socket_from_squid = -1;
29     socket_to_squid = -1;
30 }
31 
~IcmpPinger()32 IcmpPinger::~IcmpPinger()
33 {
34     Close();
35 }
36 
37 #if _SQUID_WINDOWS_
38 void
Win32SockCleanup(void)39 Win32SockCleanup(void)
40 {
41     WSACleanup();
42     return;
43 }
44 #endif
45 
46 int
Open(void)47 IcmpPinger::Open(void)
48 {
49 #if _SQUID_WINDOWS_
50 
51     WSADATA wsaData;
52     WSAPROTOCOL_INFO wpi;
53     char buf[sizeof(wpi)];
54     int x;
55 
56     struct sockaddr_in PS;
57     int xerrno;
58 
59     WSAStartup(2, &wsaData);
60     atexit(Win32SockCleanup);
61 
62     getCurrentTime();
63     _db_init(NULL, "ALL,1");
64     setmode(0, O_BINARY);
65     setmode(1, O_BINARY);
66     x = read(0, buf, sizeof(wpi));
67 
68     if (x < (int)sizeof(wpi)) {
69         xerrno = errno;
70         getCurrentTime();
71         debugs(42, DBG_CRITICAL, MYNAME << " read: FD 0: " << xstrerr(xerrno));
72         write(1, "ERR\n", 4);
73         return -1;
74     }
75 
76     memcpy(&wpi, buf, sizeof(wpi));
77 
78     write(1, "OK\n", 3);
79     x = read(0, buf, sizeof(PS));
80 
81     if (x < (int)sizeof(PS)) {
82         xerrno = errno;
83         getCurrentTime();
84         debugs(42, DBG_CRITICAL, MYNAME << " read: FD 0: " << xstrerr(xerrno));
85         write(1, "ERR\n", 4);
86         return -1;
87     }
88 
89     memcpy(&PS, buf, sizeof(PS));
90 
91     icmp_sock = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &wpi, 0, 0);
92 
93     if (icmp_sock == -1) {
94         xerrno = errno;
95         getCurrentTime();
96         debugs(42, DBG_CRITICAL, MYNAME << "WSASocket: " << xstrerr(xerrno));
97         write(1, "ERR\n", 4);
98         return -1;
99     }
100 
101     x = connect(icmp_sock, (struct sockaddr *) &PS, sizeof(PS));
102 
103     if (SOCKET_ERROR == x) {
104         xerrno = errno;
105         getCurrentTime();
106         debugs(42, DBG_CRITICAL, MYNAME << "connect: " << xstrerr(xerrno));
107         write(1, "ERR\n", 4);
108         return -1;
109     }
110 
111     write(1, "OK\n", 3);
112     memset(buf, 0, sizeof(buf));
113     x = recv(icmp_sock, (void *) buf, sizeof(buf), 0);
114 
115     if (x < 3) {
116         xerrno = errno;
117         debugs(42, DBG_CRITICAL, MYNAME << "recv: " << xstrerr(xerrno));
118         return -1;
119     }
120 
121     x = send(icmp_sock, (const void *) buf, strlen(buf), 0);
122     xerrno = errno;
123 
124     if (x < 3 || strncmp("OK\n", buf, 3)) {
125         debugs(42, DBG_CRITICAL, MYNAME << "recv: " << xstrerr(xerrno));
126         return -1;
127     }
128 
129     getCurrentTime();
130     debugs(42, DBG_IMPORTANT, "pinger: Squid socket opened");
131 
132     /* windows uses a socket stream as a dual-direction channel */
133     socket_to_squid = icmp_sock;
134     socket_from_squid = icmp_sock;
135 
136     return icmp_sock;
137 
138 #else /* !_SQUID_WINDOWS_ */
139 
140     /* non-windows apps use stdin/out pipes as the squid channel(s) */
141     socket_from_squid = 0; // use STDIN macro ??
142     socket_to_squid = 1; // use STDOUT macro ??
143     return socket_to_squid;
144 #endif
145 }
146 
147 void
Close(void)148 IcmpPinger::Close(void)
149 {
150 #if _SQUID_WINDOWS_
151 
152     shutdown(icmp_sock, SD_BOTH);
153     close(icmp_sock);
154     icmp_sock = -1;
155 #endif
156 
157     /* also shutdown the helper engines */
158     icmp4.Close();
159     icmp6.Close();
160 }
161 
162 void
Recv(void)163 IcmpPinger::Recv(void)
164 {
165     static pingerEchoData pecho;
166     int n;
167     int guess_size;
168 
169     pecho = pingerEchoData();
170     n = recv(socket_from_squid, &pecho, sizeof(pecho), 0);
171 
172     if (n < 0) {
173         debugs(42, DBG_IMPORTANT, "Pinger exiting.");
174         Close();
175         exit(1);
176     }
177 
178     if (0 == n) {
179         /* EOF indicator */
180         debugs(42, DBG_CRITICAL, "EOF encountered. Pinger exiting.");
181         errno = 0;
182         Close();
183         exit(1);
184     }
185 
186     guess_size = n - (sizeof(pingerEchoData) - PINGER_PAYLOAD_SZ);
187 
188     if (guess_size != pecho.psize) {
189         debugs(42, 2, HERE << "size mismatch, guess=" << guess_size << ", psize=" << pecho.psize);
190         /* don't process this message, but keep running */
191         return;
192     }
193 
194     /* pass request for ICMPv6 handing */
195     if (pecho.to.isIPv6()) {
196         debugs(42, 2, HERE << " Pass " << pecho.to << " off to ICMPv6 module.");
197         icmp6.SendEcho(pecho.to,
198                        pecho.opcode,
199                        pecho.payload,
200                        pecho.psize);
201     }
202 
203     /* pass the packet for ICMP handling */
204     else if (pecho.to.isIPv4()) {
205         debugs(42, 2, HERE << " Pass " << pecho.to << " off to ICMPv4 module.");
206         icmp4.SendEcho(pecho.to,
207                        pecho.opcode,
208                        pecho.payload,
209                        pecho.psize);
210     } else {
211         debugs(42, DBG_IMPORTANT, HERE << " IP has unknown Type. " << pecho.to );
212     }
213 }
214 
215 void
SendResult(pingerReplyData & preply,int len)216 IcmpPinger::SendResult(pingerReplyData &preply, int len)
217 {
218     debugs(42, 2, HERE << "return result to squid. len=" << len);
219 
220     if (send(socket_to_squid, &preply, len, 0) < 0) {
221         int xerrno = errno;
222         debugs(42, DBG_CRITICAL, "pinger: FATAL error on send: " << xstrerr(xerrno));
223         Close();
224         exit(1);
225     }
226 }
227 
228 #endif /* USE_ICMP */
229 
230