]>
Commit | Line | Data |
---|---|---|
7a2f978b | 1 | #include "squid.h" |
2 | ||
3 | static void icpLogIcp(icpUdpData *); | |
4 | static void icpHandleIcpV2(int, struct sockaddr_in, char *, int); | |
5 | ||
6 | static void | |
7 | icpLogIcp(icpUdpData * queue) | |
8 | { | |
9 | icp_common_t *header = (icp_common_t *) (void *) queue->msg; | |
10 | char *url = (char *) header + sizeof(icp_common_t); | |
11 | AccessLogEntry al; | |
7a2f978b | 12 | clientdbUpdate(queue->address.sin_addr, queue->logcode, PROTO_ICP); |
13 | if (!Config.onoff.log_udp) | |
14 | return; | |
15 | memset(&al, '\0', sizeof(AccessLogEntry)); | |
27cd7235 | 16 | al.icp.opcode = ICP_QUERY; |
7a2f978b | 17 | al.url = url; |
18 | al.cache.caddr = queue->address.sin_addr; | |
19 | al.cache.size = queue->len; | |
20 | al.cache.code = queue->logcode; | |
21 | al.cache.msec = tvSubMsec(queue->start, current_time); | |
22 | accessLogLog(&al); | |
23 | } | |
24 | ||
25 | void | |
26 | icpUdpReply(int fd, void *data) | |
27 | { | |
28 | icpUdpData *queue = data; | |
29 | int x; | |
30 | /* Disable handler, in case of errors. */ | |
31 | commSetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0); | |
79d39a72 | 32 | while ((queue = UdpQueueHead) != NULL) { |
7a2f978b | 33 | debug(12, 5) ("icpUdpReply: FD %d sending %d bytes to %s port %d\n", |
34 | fd, | |
35 | queue->len, | |
36 | inet_ntoa(queue->address.sin_addr), | |
37 | ntohs(queue->address.sin_port)); | |
f2908497 | 38 | Counter.icp.pkts_sent++; |
a7c05555 | 39 | if (queue->logcode == LOG_UDP_HIT) |
40 | Counter.icp.hits_sent++; | |
7a2f978b | 41 | x = comm_udp_sendto(fd, |
42 | &queue->address, | |
43 | sizeof(struct sockaddr_in), | |
44 | queue->msg, | |
45 | queue->len); | |
46 | if (x < 0) { | |
b224ea98 | 47 | if (ignoreErrno(errno)) |
7a2f978b | 48 | break; /* don't de-queue */ |
f2908497 | 49 | } else { |
a7c05555 | 50 | kb_incr(&Counter.icp.kbytes_sent, (size_t) x); |
7a2f978b | 51 | } |
52 | UdpQueueHead = queue->next; | |
53 | if (queue->logcode) | |
54 | icpLogIcp(queue); | |
55 | safe_free(queue->msg); | |
56 | safe_free(queue); | |
57 | } | |
58 | /* Reinstate handler if needed */ | |
59 | if (UdpQueueHead) { | |
60 | commSetSelect(fd, COMM_SELECT_WRITE, icpUdpReply, UdpQueueHead, 0); | |
61 | } | |
62 | } | |
63 | ||
64 | void * | |
65 | icpCreateMessage( | |
66 | icp_opcode opcode, | |
67 | int flags, | |
68 | const char *url, | |
69 | int reqnum, | |
70 | int pad) | |
71 | { | |
72 | char *buf = NULL; | |
73 | icp_common_t *headerp = NULL; | |
74 | char *urloffset = NULL; | |
75 | int buf_len; | |
76 | buf_len = sizeof(icp_common_t) + strlen(url) + 1; | |
27cd7235 | 77 | if (opcode == ICP_QUERY) |
7a2f978b | 78 | buf_len += sizeof(u_num32); |
79 | buf = xcalloc(buf_len, 1); | |
80 | headerp = (icp_common_t *) (void *) buf; | |
79d39a72 | 81 | headerp->opcode = (char) opcode; |
7a2f978b | 82 | headerp->version = ICP_VERSION_CURRENT; |
79d39a72 | 83 | headerp->length = (u_short) htons(buf_len); |
7a2f978b | 84 | headerp->reqnum = htonl(reqnum); |
85 | headerp->flags = htonl(flags); | |
86 | headerp->pad = htonl(pad); | |
87 | headerp->shostid = htonl(theOutICPAddr.s_addr); | |
88 | urloffset = buf + sizeof(icp_common_t); | |
27cd7235 | 89 | if (opcode == ICP_QUERY) |
7a2f978b | 90 | urloffset += sizeof(u_num32); |
91 | xmemcpy(urloffset, url, strlen(url)); | |
92 | return buf; | |
93 | } | |
94 | ||
7a2f978b | 95 | void |
96 | icpUdpSend(int fd, | |
97 | const struct sockaddr_in *to, | |
98 | icp_common_t * msg, | |
99 | log_type logcode, | |
100 | protocol_t proto) | |
101 | { | |
102 | icpUdpData *data = xcalloc(1, sizeof(icpUdpData)); | |
103 | debug(12, 4) ("icpUdpSend: Queueing %s for %s\n", | |
27cd7235 | 104 | icp_opcode_str[msg->opcode], |
7a2f978b | 105 | inet_ntoa(to->sin_addr)); |
106 | data->address = *to; | |
107 | data->msg = msg; | |
108 | data->len = (int) ntohs(msg->length); | |
a7c05555 | 109 | data->start = current_time; |
7a2f978b | 110 | data->logcode = logcode; |
111 | data->proto = proto; | |
112 | AppendUdp(data); | |
113 | commSetSelect(fd, COMM_SELECT_WRITE, icpUdpReply, UdpQueueHead, 0); | |
114 | } | |
115 | ||
116 | int | |
117 | icpCheckUdpHit(StoreEntry * e, request_t * request) | |
118 | { | |
119 | if (e == NULL) | |
120 | return 0; | |
121 | if (!storeEntryValidToSend(e)) | |
122 | return 0; | |
123 | if (Config.onoff.icp_hit_stale) | |
124 | return 1; | |
125 | if (refreshCheck(e, request, 30)) | |
126 | return 0; | |
7a2f978b | 127 | return 1; |
128 | } | |
7a2f978b | 129 | |
130 | static void | |
131 | icpHandleIcpV2(int fd, struct sockaddr_in from, char *buf, int len) | |
132 | { | |
133 | icp_common_t header; | |
134 | icp_common_t *headerp = (icp_common_t *) (void *) buf; | |
135 | StoreEntry *entry = NULL; | |
136 | char *url = NULL; | |
137 | const cache_key *key; | |
138 | request_t *icp_request = NULL; | |
139 | int allow = 0; | |
7a2f978b | 140 | aclCheck_t checklist; |
141 | icp_common_t *reply; | |
142 | int src_rtt = 0; | |
143 | u_num32 flags = 0; | |
144 | header.opcode = headerp->opcode; | |
145 | header.version = headerp->version; | |
146 | header.length = ntohs(headerp->length); | |
147 | header.reqnum = ntohl(headerp->reqnum); | |
148 | header.flags = ntohl(headerp->flags); | |
149 | header.shostid = ntohl(headerp->shostid); | |
150 | header.pad = ntohl(headerp->pad); | |
151 | ||
152 | switch (header.opcode) { | |
27cd7235 | 153 | case ICP_QUERY: |
7a2f978b | 154 | /* We have a valid packet */ |
155 | url = buf + sizeof(header) + sizeof(u_num32); | |
156 | if ((icp_request = urlParse(METHOD_GET, url)) == NULL) { | |
27cd7235 | 157 | reply = icpCreateMessage(ICP_ERR, 0, url, header.reqnum, 0); |
7a2f978b | 158 | icpUdpSend(fd, &from, reply, LOG_UDP_INVALID, PROTO_NONE); |
159 | break; | |
160 | } | |
161 | checklist.src_addr = from.sin_addr; | |
162 | checklist.request = icp_request; | |
163 | allow = aclCheckFast(Config.accessList.icp, &checklist); | |
164 | if (!allow) { | |
165 | debug(12, 2) ("icpHandleIcpV2: Access Denied for %s by %s.\n", | |
166 | inet_ntoa(from.sin_addr), AclMatchedName); | |
167 | if (clientdbDeniedPercent(from.sin_addr) < 95) { | |
27cd7235 | 168 | reply = icpCreateMessage(ICP_DENIED, 0, url, header.reqnum, 0); |
7a2f978b | 169 | icpUdpSend(fd, &from, reply, LOG_UDP_DENIED, icp_request->protocol); |
1a47dc5a | 170 | } else { |
171 | /* | |
172 | * count this DENIED query in the clientdb, even though | |
173 | * we're not sending an ICP reply... | |
174 | */ | |
175 | clientdbUpdate(from.sin_addr, | |
176 | LOG_UDP_DENIED, | |
177 | Config.Port.icp); | |
7a2f978b | 178 | } |
179 | break; | |
180 | } | |
181 | if (header.flags & ICP_FLAG_SRC_RTT) { | |
182 | int rtt = netdbHostRtt(icp_request->host); | |
183 | int hops = netdbHostHops(icp_request->host); | |
184 | src_rtt = ((hops & 0xFFFF) << 16) | (rtt & 0xFFFF); | |
185 | if (rtt) | |
186 | flags |= ICP_FLAG_SRC_RTT; | |
187 | } | |
188 | /* The peer is allowed to use this cache */ | |
189 | key = storeKeyPublic(url, METHOD_GET); | |
190 | entry = storeGet(key); | |
27cd7235 | 191 | debug(12, 5) ("icpHandleIcpV2: OPCODE %s\n", icp_opcode_str[header.opcode]); |
7a2f978b | 192 | if (icpCheckUdpHit(entry, icp_request)) { |
067bea91 | 193 | reply = icpCreateMessage(ICP_HIT, flags, url, header.reqnum, src_rtt); |
194 | icpUdpSend(fd, &from, reply, LOG_UDP_HIT, icp_request->protocol); | |
195 | break; | |
7a2f978b | 196 | } |
197 | /* if store is rebuilding, return a UDP_HIT, but not a MISS */ | |
198 | if (store_rebuilding && opt_reload_hit_only) { | |
27cd7235 | 199 | reply = icpCreateMessage(ICP_MISS_NOFETCH, flags, url, header.reqnum, src_rtt); |
7a2f978b | 200 | icpUdpSend(fd, &from, reply, LOG_UDP_MISS_NOFETCH, icp_request->protocol); |
201 | } else if (hit_only_mode_until > squid_curtime) { | |
27cd7235 | 202 | reply = icpCreateMessage(ICP_MISS_NOFETCH, flags, url, header.reqnum, src_rtt); |
7a2f978b | 203 | icpUdpSend(fd, &from, reply, LOG_UDP_MISS_NOFETCH, icp_request->protocol); |
204 | } else { | |
27cd7235 | 205 | reply = icpCreateMessage(ICP_MISS, flags, url, header.reqnum, src_rtt); |
7a2f978b | 206 | icpUdpSend(fd, &from, reply, LOG_UDP_MISS, icp_request->protocol); |
207 | } | |
208 | break; | |
209 | ||
27cd7235 | 210 | case ICP_HIT: |
a7c05555 | 211 | Counter.icp.hits_recv++; |
27cd7235 | 212 | case ICP_SECHO: |
213 | case ICP_DECHO: | |
214 | case ICP_MISS: | |
215 | case ICP_DENIED: | |
216 | case ICP_MISS_NOFETCH: | |
7a2f978b | 217 | if (neighbors_do_private_keys && header.reqnum == 0) { |
218 | debug(12, 0) ("icpHandleIcpV2: Neighbor %s returned reqnum = 0\n", | |
219 | inet_ntoa(from.sin_addr)); | |
220 | debug(12, 0) ("icpHandleIcpV2: Disabling use of private keys\n"); | |
221 | neighbors_do_private_keys = 0; | |
222 | } | |
223 | url = buf + sizeof(header); | |
7a2f978b | 224 | debug(12, 3) ("icpHandleIcpV2: %s from %s for '%s'\n", |
27cd7235 | 225 | icp_opcode_str[header.opcode], |
7a2f978b | 226 | inet_ntoa(from.sin_addr), |
227 | url); | |
228 | if (neighbors_do_private_keys && header.reqnum) | |
229 | key = storeKeyPrivate(url, METHOD_GET, header.reqnum); | |
230 | else | |
231 | key = storeKeyPublic(url, METHOD_GET); | |
232 | debug(12, 3) ("icpHandleIcpV2: Looking for key '%s'\n", | |
233 | storeKeyText(key)); | |
234 | if ((entry = storeGet(key)) == NULL) { | |
235 | debug(12, 3) ("icpHandleIcpV2: Ignoring %s for NULL Entry.\n", | |
27cd7235 | 236 | icp_opcode_str[header.opcode]); |
7a2f978b | 237 | } else { |
238 | /* call neighborsUdpAck even if ping_status != PING_WAITING */ | |
79d39a72 | 239 | neighborsUdpAck(url, &header, &from, entry); |
7a2f978b | 240 | } |
241 | break; | |
242 | ||
27cd7235 | 243 | case ICP_INVALID: |
244 | case ICP_ERR: | |
7a2f978b | 245 | break; |
246 | ||
247 | default: | |
248 | debug(12, 0) ("icpHandleIcpV2: UNKNOWN OPCODE: %d from %s\n", | |
249 | header.opcode, inet_ntoa(from.sin_addr)); | |
250 | break; | |
251 | } | |
252 | if (icp_request) | |
3f6c0fb2 | 253 | memFree(MEM_REQUEST_T, icp_request); |
7a2f978b | 254 | } |
255 | ||
256 | #ifdef ICP_PKT_DUMP | |
257 | static void | |
258 | icpPktDump(icp_common_t * pkt) | |
259 | { | |
260 | struct in_addr a; | |
261 | ||
262 | debug(12, 9) ("opcode: %3d %s\n", | |
263 | (int) pkt->opcode, | |
27cd7235 | 264 | icp_opcode_str[pkt->opcode]); |
7a2f978b | 265 | debug(12, 9) ("version: %-8d\n", (int) pkt->version); |
266 | debug(12, 9) ("length: %-8d\n", (int) ntohs(pkt->length)); | |
267 | debug(12, 9) ("reqnum: %-8d\n", ntohl(pkt->reqnum)); | |
268 | debug(12, 9) ("flags: %-8x\n", ntohl(pkt->flags)); | |
269 | a.s_addr = ntohl(pkt->shostid); | |
270 | debug(12, 9) ("shostid: %s\n", inet_ntoa(a)); | |
271 | debug(12, 9) ("payload: %s\n", (char *) pkt + sizeof(icp_common_t)); | |
272 | } | |
273 | #endif | |
274 | ||
275 | void | |
79d39a72 | 276 | icpHandleUdp(int sock, void *datanotused) |
7a2f978b | 277 | { |
278 | struct sockaddr_in from; | |
279 | int from_len; | |
280 | LOCAL_ARRAY(char, buf, SQUID_UDP_SO_RCVBUF); | |
281 | int len; | |
282 | icp_common_t *headerp = NULL; | |
283 | int icp_version; | |
284 | ||
285 | commSetSelect(sock, COMM_SELECT_READ, icpHandleUdp, NULL, 0); | |
286 | from_len = sizeof(from); | |
287 | memset(&from, '\0', from_len); | |
288 | len = recvfrom(sock, | |
289 | buf, | |
290 | SQUID_UDP_SO_RCVBUF - 1, | |
291 | 0, | |
292 | (struct sockaddr *) &from, | |
293 | &from_len); | |
294 | if (len < 0) { | |
295 | #ifdef _SQUID_LINUX_ | |
296 | /* Some Linux systems seem to set the FD for reading and then | |
297 | * return ECONNREFUSED when sendto() fails and generates an ICMP | |
298 | * port unreachable message. */ | |
299 | /* or maybe an EHOSTUNREACH "No route to host" message */ | |
300 | if (errno != ECONNREFUSED && errno != EHOSTUNREACH) | |
301 | #endif | |
302 | debug(50, 1) ("icpHandleUdp: FD %d recvfrom: %s\n", | |
303 | sock, xstrerror()); | |
304 | return; | |
305 | } | |
f2908497 | 306 | Counter.icp.pkts_recv++; |
a7c05555 | 307 | kb_incr(&Counter.icp.kbytes_recv, (size_t) len); |
7a2f978b | 308 | buf[len] = '\0'; |
309 | debug(12, 4) ("icpHandleUdp: FD %d: received %d bytes from %s.\n", | |
310 | sock, | |
311 | len, | |
312 | inet_ntoa(from.sin_addr)); | |
313 | #ifdef ICP_PACKET_DUMP | |
314 | icpPktDump(buf); | |
315 | #endif | |
316 | if (len < sizeof(icp_common_t)) { | |
317 | debug(12, 4) ("icpHandleUdp: Ignoring too-small UDP packet\n"); | |
318 | return; | |
319 | } | |
320 | headerp = (icp_common_t *) (void *) buf; | |
321 | if ((icp_version = (int) headerp->version) == ICP_VERSION_2) | |
322 | icpHandleIcpV2(sock, from, buf, len); | |
323 | else if (icp_version == ICP_VERSION_3) | |
324 | icpHandleIcpV3(sock, from, buf, len); | |
325 | else | |
326 | debug(12, 0) ("WARNING: Unused ICP version %d received from %s:%d\n", | |
327 | icp_version, | |
328 | inet_ntoa(from.sin_addr), | |
329 | ntohs(from.sin_port)); | |
330 | } | |
15df8349 | 331 | |
332 | void | |
333 | icpConnectionsOpen(void) | |
334 | { | |
335 | u_short port; | |
336 | struct in_addr addr; | |
337 | struct sockaddr_in xaddr; | |
338 | int x; | |
339 | int len; | |
340 | wordlist *s; | |
341 | if (Config2.Accel.on && !Config.onoff.accel_with_proxy) | |
342 | return; | |
343 | if ((port = Config.Port.icp) <= 0) | |
344 | return; | |
345 | enter_suid(); | |
346 | theInIcpConnection = comm_open(SOCK_DGRAM, | |
347 | 0, | |
348 | Config.Addrs.udp_incoming, | |
349 | port, | |
350 | COMM_NONBLOCKING, | |
351 | "ICP Port"); | |
352 | leave_suid(); | |
353 | if (theInIcpConnection < 0) | |
354 | fatal("Cannot open ICP Port"); | |
355 | fd_note(theInIcpConnection, "ICP socket"); | |
356 | commSetSelect(theInIcpConnection, | |
357 | COMM_SELECT_READ, | |
358 | icpHandleUdp, | |
359 | NULL, 0); | |
360 | for (s = Config.mcast_group_list; s; s = s->next) | |
361 | ipcache_nbgethostbyname(s->key, mcastJoinGroups, NULL); | |
1e948d78 | 362 | debug(1, 1) ("Accepting ICP messages on port %d, FD %d.\n", |
15df8349 | 363 | (int) port, theInIcpConnection); |
364 | if ((addr = Config.Addrs.udp_outgoing).s_addr != no_addr.s_addr) { | |
365 | enter_suid(); | |
366 | theOutIcpConnection = comm_open(SOCK_DGRAM, | |
367 | 0, | |
368 | addr, | |
369 | port, | |
370 | COMM_NONBLOCKING, | |
371 | "ICP Port"); | |
372 | leave_suid(); | |
373 | if (theOutIcpConnection < 0) | |
374 | fatal("Cannot open Outgoing ICP Port"); | |
375 | commSetSelect(theOutIcpConnection, | |
376 | COMM_SELECT_READ, | |
377 | icpHandleUdp, | |
378 | NULL, 0); | |
1e948d78 | 379 | debug(1, 1) ("Outgoing ICP messages on port %d, FD %d.\n", |
380 | (int) port, theOutIcpConnection); | |
15df8349 | 381 | fd_note(theOutIcpConnection, "Outgoing ICP socket"); |
382 | fd_note(theInIcpConnection, "Incoming ICP socket"); | |
383 | } else { | |
384 | theOutIcpConnection = theInIcpConnection; | |
385 | } | |
386 | memset(&theOutICPAddr, '\0', sizeof(struct in_addr)); | |
387 | len = sizeof(struct sockaddr_in); | |
388 | memset(&xaddr, '\0', len); | |
389 | x = getsockname(theOutIcpConnection, | |
390 | (struct sockaddr *) &xaddr, &len); | |
391 | if (x < 0) | |
392 | debug(50, 1) ("theOutIcpConnection FD %d: getsockname: %s\n", | |
393 | theOutIcpConnection, xstrerror()); | |
394 | else | |
395 | theOutICPAddr = xaddr.sin_addr; | |
396 | } | |
c0fbae16 | 397 | |
398 | void | |
399 | icpConnectionsClose(void) | |
400 | { | |
401 | if (theInIcpConnection < 0) | |
402 | return; | |
403 | debug(1, 1) ("FD %d Closing ICP connection\n", theInIcpConnection); | |
404 | if (theInIcpConnection != theOutIcpConnection) | |
405 | comm_close(theInIcpConnection); | |
406 | /* | |
407 | * Here we set 'theInIcpConnection' to -1 even though the ICP 'in' | |
408 | * and 'out' sockets might be just one FD. This prevents this | |
409 | * function from executing repeatedly. When we are really ready to | |
410 | * exit or restart, main will comm_close the 'out' descriptor. | |
411 | */ | |
412 | theInIcpConnection = -1; | |
413 | /* | |
414 | * Normally we only write to the outgoing ICP socket, but | |
415 | * we also have a read handler there to catch messages sent | |
416 | * to that specific interface. During shutdown, we must | |
417 | * disable reading on the outgoing socket. | |
418 | */ | |
419 | assert(theOutIcpConnection > -1); | |
420 | commSetSelect(theOutIcpConnection, COMM_SELECT_READ, NULL, NULL, 0); | |
421 | } |