]> git.ipfire.org Git - thirdparty/squid.git/blame - src/icp_v2.cc
trying to rearrange file names so they make more sense.
[thirdparty/squid.git] / src / icp_v2.cc
CommitLineData
7a2f978b 1#include "squid.h"
2
3static void icpLogIcp(icpUdpData *);
4static void icpHandleIcpV2(int, struct sockaddr_in, char *, int);
5
6static void
7icpLogIcp(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;
12 ICPCacheInfo->proto_touchobject(ICPCacheInfo,
13 queue->proto,
14 queue->len);
15 ICPCacheInfo->proto_count(ICPCacheInfo,
16 queue->proto,
17 queue->logcode);
18 clientdbUpdate(queue->address.sin_addr, queue->logcode, PROTO_ICP);
19 if (!Config.onoff.log_udp)
20 return;
21 memset(&al, '\0', sizeof(AccessLogEntry));
22 al.icp.opcode = ICP_OP_QUERY;
23 al.url = url;
24 al.cache.caddr = queue->address.sin_addr;
25 al.cache.size = queue->len;
26 al.cache.code = queue->logcode;
27 al.cache.msec = tvSubMsec(queue->start, current_time);
28 accessLogLog(&al);
29}
30
31void
32icpUdpReply(int fd, void *data)
33{
34 icpUdpData *queue = data;
35 int x;
36 /* Disable handler, in case of errors. */
37 commSetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
38 while ((queue = UdpQueueHead)) {
39 debug(12, 5) ("icpUdpReply: FD %d sending %d bytes to %s port %d\n",
40 fd,
41 queue->len,
42 inet_ntoa(queue->address.sin_addr),
43 ntohs(queue->address.sin_port));
44 x = comm_udp_sendto(fd,
45 &queue->address,
46 sizeof(struct sockaddr_in),
47 queue->msg,
48 queue->len);
49 if (x < 0) {
50 if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
51 break; /* don't de-queue */
52 }
53 UdpQueueHead = queue->next;
54 if (queue->logcode)
55 icpLogIcp(queue);
56 safe_free(queue->msg);
57 safe_free(queue);
58 }
59 /* Reinstate handler if needed */
60 if (UdpQueueHead) {
61 commSetSelect(fd, COMM_SELECT_WRITE, icpUdpReply, UdpQueueHead, 0);
62 }
63}
64
65void *
66icpCreateMessage(
67 icp_opcode opcode,
68 int flags,
69 const char *url,
70 int reqnum,
71 int pad)
72{
73 char *buf = NULL;
74 icp_common_t *headerp = NULL;
75 char *urloffset = NULL;
76 int buf_len;
77 buf_len = sizeof(icp_common_t) + strlen(url) + 1;
78 assert(reqnum);
79 if (opcode == ICP_OP_QUERY)
80 buf_len += sizeof(u_num32);
81 buf = xcalloc(buf_len, 1);
82 headerp = (icp_common_t *) (void *) buf;
83 headerp->opcode = opcode;
84 headerp->version = ICP_VERSION_CURRENT;
85 headerp->length = htons(buf_len);
86 headerp->reqnum = htonl(reqnum);
87 headerp->flags = htonl(flags);
88 headerp->pad = htonl(pad);
89 headerp->shostid = htonl(theOutICPAddr.s_addr);
90 urloffset = buf + sizeof(icp_common_t);
91 if (opcode == ICP_OP_QUERY)
92 urloffset += sizeof(u_num32);
93 xmemcpy(urloffset, url, strlen(url));
94 return buf;
95}
96
97#if USE_ICP_HIT_OBJ
98static void *
99icpCreateHitObjMessage(
100 icp_opcode opcode,
101 int flags,
102 const char *url,
103 int reqnum,
104 int pad,
105 StoreEntry * entry)
106{
107 char *buf = NULL;
108 char *entryoffset = NULL;
109 char *urloffset = NULL;
110 icp_common_t *headerp = NULL;
111 int buf_len;
112 u_short data_sz;
113 int size;
114 MemObject *m = entry->mem_obj;
115 assert(m != NULL);
116 buf_len = sizeof(icp_common_t) + strlen(url) + 1 + 2 + entry->object_len;
117 if (opcode == ICP_OP_QUERY)
118 buf_len += sizeof(u_num32);
119 buf = xcalloc(buf_len, 1);
120 headerp = (icp_common_t *) (void *) buf;
121 headerp->opcode = opcode;
122 headerp->version = ICP_VERSION_CURRENT;
123 headerp->length = htons(buf_len);
124 headerp->reqnum = htonl(reqnum);
125 headerp->flags = htonl(flags);
126 headerp->pad = htonl(pad);
127 headerp->shostid = htonl(theOutICPAddr.s_addr);
128 urloffset = buf + sizeof(icp_common_t);
129 xmemcpy(urloffset, url, strlen(url));
130 data_sz = htons((u_short) entry->object_len);
131 entryoffset = urloffset + strlen(url) + 1;
132 xmemcpy(entryoffset, &data_sz, sizeof(u_short));
133 entryoffset += sizeof(u_short);
134 assert(m->data != NULL);
135 size = memCopy(m->data, 0, entryoffset, entry->object_len);
136 if (size < 0 || size != entry->object_len) {
137 debug(12, 1) ("icpCreateHitObjMessage: copy failed, wanted %d got %d bytes\n",
138 entry->object_len, size);
139 safe_free(buf);
140 return NULL;
141 }
142 return buf;
143}
144#endif
145
146void
147icpUdpSend(int fd,
148 const struct sockaddr_in *to,
149 icp_common_t * msg,
150 log_type logcode,
151 protocol_t proto)
152{
153 icpUdpData *data = xcalloc(1, sizeof(icpUdpData));
154 debug(12, 4) ("icpUdpSend: Queueing %s for %s\n",
155 IcpOpcodeStr[msg->opcode],
156 inet_ntoa(to->sin_addr));
157 data->address = *to;
158 data->msg = msg;
159 data->len = (int) ntohs(msg->length);
160#ifndef LESS_TIMING
161 data->start = current_time; /* wrong for HIT_OBJ */
162#endif
163 data->logcode = logcode;
164 data->proto = proto;
165 AppendUdp(data);
166 commSetSelect(fd, COMM_SELECT_WRITE, icpUdpReply, UdpQueueHead, 0);
167}
168
169int
170icpCheckUdpHit(StoreEntry * e, request_t * request)
171{
172 if (e == NULL)
173 return 0;
174 if (!storeEntryValidToSend(e))
175 return 0;
176 if (Config.onoff.icp_hit_stale)
177 return 1;
178 if (refreshCheck(e, request, 30))
179 return 0;
180 /* MUST NOT do UDP_HIT_OBJ if object is not in memory with async_io. The */
181 /* icpHandleV2 code has not been written to support it - squid will die! */
182#if USE_ASYNC_IO || defined(MEM_UDP_HIT_OBJ)
183 if (e->mem_status != IN_MEMORY)
184 return 0;
185#endif
186 return 1;
187}
188
189#if USE_ICP_HIT_OBJ
190int
191icpCheckUdpHitObj(StoreEntry * e, request_t * r, icp_common_t * h, int len)
192{
193 if (!BIT_TEST(h->flags, ICP_FLAG_HIT_OBJ)) /* not requested */
194 return 0;
195 if (len > Config.udpMaxHitObjsz) /* too big */
196 return 0;
197 if (refreshCheck(e, r, 0)) /* stale */
198 return 0;
199#ifdef MEM_UDP_HIT_OBJ
200 if (e->mem_status != IN_MEMORY)
201 return 0;
202#endif
203 return 1;
204}
205#endif
206
207static void
208icpHandleIcpV2(int fd, struct sockaddr_in from, char *buf, int len)
209{
210 icp_common_t header;
211 icp_common_t *headerp = (icp_common_t *) (void *) buf;
212 StoreEntry *entry = NULL;
213 char *url = NULL;
214 const cache_key *key;
215 request_t *icp_request = NULL;
216 int allow = 0;
217 char *data = NULL;
218 u_short data_sz = 0;
219 u_short u;
220 int pkt_len;
221 aclCheck_t checklist;
222 icp_common_t *reply;
223 int src_rtt = 0;
224 u_num32 flags = 0;
225 header.opcode = headerp->opcode;
226 header.version = headerp->version;
227 header.length = ntohs(headerp->length);
228 header.reqnum = ntohl(headerp->reqnum);
229 header.flags = ntohl(headerp->flags);
230 header.shostid = ntohl(headerp->shostid);
231 header.pad = ntohl(headerp->pad);
232
233 switch (header.opcode) {
234 case ICP_OP_QUERY:
235 nudpconn++;
236 /* We have a valid packet */
237 url = buf + sizeof(header) + sizeof(u_num32);
238 if ((icp_request = urlParse(METHOD_GET, url)) == NULL) {
239 reply = icpCreateMessage(ICP_OP_ERR, 0, url, header.reqnum, 0);
240 icpUdpSend(fd, &from, reply, LOG_UDP_INVALID, PROTO_NONE);
241 break;
242 }
243 checklist.src_addr = from.sin_addr;
244 checklist.request = icp_request;
245 allow = aclCheckFast(Config.accessList.icp, &checklist);
246 if (!allow) {
247 debug(12, 2) ("icpHandleIcpV2: Access Denied for %s by %s.\n",
248 inet_ntoa(from.sin_addr), AclMatchedName);
249 if (clientdbDeniedPercent(from.sin_addr) < 95) {
250 reply = icpCreateMessage(ICP_OP_DENIED, 0, url, header.reqnum, 0);
251 icpUdpSend(fd, &from, reply, LOG_UDP_DENIED, icp_request->protocol);
252 }
253 break;
254 }
255 if (header.flags & ICP_FLAG_SRC_RTT) {
256 int rtt = netdbHostRtt(icp_request->host);
257 int hops = netdbHostHops(icp_request->host);
258 src_rtt = ((hops & 0xFFFF) << 16) | (rtt & 0xFFFF);
259 if (rtt)
260 flags |= ICP_FLAG_SRC_RTT;
261 }
262 /* The peer is allowed to use this cache */
263 key = storeKeyPublic(url, METHOD_GET);
264 entry = storeGet(key);
265 debug(12, 5) ("icpHandleIcpV2: OPCODE %s\n", IcpOpcodeStr[header.opcode]);
266 if (icpCheckUdpHit(entry, icp_request)) {
267 pkt_len = sizeof(icp_common_t) + strlen(url) + 1 + 2 + entry->object_len;
268#if USE_ICP_HIT_OBJ
269 if (icpCheckUdpHitObj(entry, icp_request, &header, pkt_len)) {
270 reply = icpCreateHitObjMessage(ICP_OP_HIT_OBJ,
271 flags,
272 url,
273 header.reqnum,
274 src_rtt,
275 entry);
276 icpUdpSend(fd, &from, reply, LOG_UDP_HIT, icp_request->protocol);
277 break;
278 } else {
279#endif
280 reply = icpCreateMessage(ICP_OP_HIT, flags, url, header.reqnum, src_rtt);
281 icpUdpSend(fd, &from, reply, LOG_UDP_HIT, icp_request->protocol);
282 break;
283#if USE_ICP_HIT_OBJ
284 }
285#endif
286 }
287 /* if store is rebuilding, return a UDP_HIT, but not a MISS */
288 if (store_rebuilding && opt_reload_hit_only) {
289 reply = icpCreateMessage(ICP_OP_MISS_NOFETCH, flags, url, header.reqnum, src_rtt);
290 icpUdpSend(fd, &from, reply, LOG_UDP_MISS_NOFETCH, icp_request->protocol);
291 } else if (hit_only_mode_until > squid_curtime) {
292 reply = icpCreateMessage(ICP_OP_MISS_NOFETCH, flags, url, header.reqnum, src_rtt);
293 icpUdpSend(fd, &from, reply, LOG_UDP_MISS_NOFETCH, icp_request->protocol);
294 } else {
295 reply = icpCreateMessage(ICP_OP_MISS, flags, url, header.reqnum, src_rtt);
296 icpUdpSend(fd, &from, reply, LOG_UDP_MISS, icp_request->protocol);
297 }
298 break;
299
300 case ICP_OP_HIT_OBJ:
301 case ICP_OP_HIT:
302 case ICP_OP_SECHO:
303 case ICP_OP_DECHO:
304 case ICP_OP_MISS:
305 case ICP_OP_DENIED:
306 case ICP_OP_MISS_NOFETCH:
307 if (neighbors_do_private_keys && header.reqnum == 0) {
308 debug(12, 0) ("icpHandleIcpV2: Neighbor %s returned reqnum = 0\n",
309 inet_ntoa(from.sin_addr));
310 debug(12, 0) ("icpHandleIcpV2: Disabling use of private keys\n");
311 neighbors_do_private_keys = 0;
312 }
313 url = buf + sizeof(header);
314 if (header.opcode == ICP_OP_HIT_OBJ) {
315 data = url + strlen(url) + 1;
316 xmemcpy((char *) &u, data, sizeof(u_short));
317 data += sizeof(u_short);
318 data_sz = ntohs(u);
319 if ((int) data_sz > (len - (data - buf))) {
320 debug(12, 0) ("icpHandleIcpV2: ICP_OP_HIT_OBJ object too small\n");
321 break;
322 }
323 }
324 debug(12, 3) ("icpHandleIcpV2: %s from %s for '%s'\n",
325 IcpOpcodeStr[header.opcode],
326 inet_ntoa(from.sin_addr),
327 url);
328 if (neighbors_do_private_keys && header.reqnum)
329 key = storeKeyPrivate(url, METHOD_GET, header.reqnum);
330 else
331 key = storeKeyPublic(url, METHOD_GET);
332 debug(12, 3) ("icpHandleIcpV2: Looking for key '%s'\n",
333 storeKeyText(key));
334 if ((entry = storeGet(key)) == NULL) {
335 debug(12, 3) ("icpHandleIcpV2: Ignoring %s for NULL Entry.\n",
336 IcpOpcodeStr[header.opcode]);
337 } else {
338 /* call neighborsUdpAck even if ping_status != PING_WAITING */
339 neighborsUdpAck(fd,
340 url,
341 &header,
342 &from,
343 entry,
344 data,
345 (int) data_sz);
346 }
347 break;
348
349 case ICP_OP_INVALID:
350 case ICP_OP_ERR:
351 break;
352
353 default:
354 debug(12, 0) ("icpHandleIcpV2: UNKNOWN OPCODE: %d from %s\n",
355 header.opcode, inet_ntoa(from.sin_addr));
356 break;
357 }
358 if (icp_request)
359 put_free_request_t(icp_request);
360}
361
362#ifdef ICP_PKT_DUMP
363static void
364icpPktDump(icp_common_t * pkt)
365{
366 struct in_addr a;
367
368 debug(12, 9) ("opcode: %3d %s\n",
369 (int) pkt->opcode,
370 IcpOpcodeStr[pkt->opcode]);
371 debug(12, 9) ("version: %-8d\n", (int) pkt->version);
372 debug(12, 9) ("length: %-8d\n", (int) ntohs(pkt->length));
373 debug(12, 9) ("reqnum: %-8d\n", ntohl(pkt->reqnum));
374 debug(12, 9) ("flags: %-8x\n", ntohl(pkt->flags));
375 a.s_addr = ntohl(pkt->shostid);
376 debug(12, 9) ("shostid: %s\n", inet_ntoa(a));
377 debug(12, 9) ("payload: %s\n", (char *) pkt + sizeof(icp_common_t));
378}
379#endif
380
381void
382icpHandleUdp(int sock, void *not_used)
383{
384 struct sockaddr_in from;
385 int from_len;
386 LOCAL_ARRAY(char, buf, SQUID_UDP_SO_RCVBUF);
387 int len;
388 icp_common_t *headerp = NULL;
389 int icp_version;
390
391 commSetSelect(sock, COMM_SELECT_READ, icpHandleUdp, NULL, 0);
392 from_len = sizeof(from);
393 memset(&from, '\0', from_len);
394 len = recvfrom(sock,
395 buf,
396 SQUID_UDP_SO_RCVBUF - 1,
397 0,
398 (struct sockaddr *) &from,
399 &from_len);
400 if (len < 0) {
401#ifdef _SQUID_LINUX_
402 /* Some Linux systems seem to set the FD for reading and then
403 * return ECONNREFUSED when sendto() fails and generates an ICMP
404 * port unreachable message. */
405 /* or maybe an EHOSTUNREACH "No route to host" message */
406 if (errno != ECONNREFUSED && errno != EHOSTUNREACH)
407#endif
408 debug(50, 1) ("icpHandleUdp: FD %d recvfrom: %s\n",
409 sock, xstrerror());
410 return;
411 }
412 buf[len] = '\0';
413 debug(12, 4) ("icpHandleUdp: FD %d: received %d bytes from %s.\n",
414 sock,
415 len,
416 inet_ntoa(from.sin_addr));
417#ifdef ICP_PACKET_DUMP
418 icpPktDump(buf);
419#endif
420 if (len < sizeof(icp_common_t)) {
421 debug(12, 4) ("icpHandleUdp: Ignoring too-small UDP packet\n");
422 return;
423 }
424 headerp = (icp_common_t *) (void *) buf;
425 if ((icp_version = (int) headerp->version) == ICP_VERSION_2)
426 icpHandleIcpV2(sock, from, buf, len);
427 else if (icp_version == ICP_VERSION_3)
428 icpHandleIcpV3(sock, from, buf, len);
429 else
430 debug(12, 0) ("WARNING: Unused ICP version %d received from %s:%d\n",
431 icp_version,
432 inet_ntoa(from.sin_addr),
433 ntohs(from.sin_port));
434}