/* Web Polygraph http://www.web-polygraph.org/ * (C) 2003-2006 The Measurement Factory * Licensed under the Apache License, Version 2.0 */ #include "base/polygraph.h" #include "xstd/h/string.h" #include "xstd/h/iostream.h" #include "xstd/h/sstream.h" #include "xstd/Socket.h" #include "runtime/AddrMap.h" #include "runtime/HostMap.h" #include "runtime/PubWorld.h" #include "runtime/ErrorMgr.h" #include "runtime/httpHdrs.h" #include "runtime/LogComment.h" #include "runtime/polyErrors.h" #include "runtime/globals.h" #include "csm/oid2Url.h" #include "icp/IcpMsg.h" struct IcpRawMsg { typedef unsigned int u_num32; // XXX: check C++ standard and/or move this to config.h! unsigned int opCode:8; unsigned int version:8; unsigned int length:16; u_num32 reqNum; u_num32 _options; u_num32 _optData; u_num32 _senderHost; char buf[16*1024-5*32]; }; IcpMsg::IcpMsg(): theReqNum(-1), theOpCode(icpInvalid) { } bool IcpMsg::send(Socket &s) const { Assert(theAddr); static IcpRawMsg msg; const Size hdrSize = SizeOf(msg) - SizeOf(msg.buf); const Size urlOff = theOpCode == icpQuery ? SizeOf(msg._senderHost) : (Size)0; const Size bufLen = SizeOf(msg.buf) - urlOff - (Size)1; char *url = msg.buf + urlOff; memset(&msg, 0, SizeOf(msg)); ofixedstream os(url, bufLen); Oid2Url(theOid, os); os << ends; const Size urlLen = strlen(url); msg.opCode = theOpCode; msg.version = 2; msg.length = hdrSize + urlOff + urlLen + (Size)1; msg.reqNum = (IcpRawMsg::u_num32) theReqNum; // preserve length const Size msgLen = (Size)msg.length; // ntoh msg.length = htons(msg.length); msg.reqNum = htonl(msg.reqNum); // we do not care about other fields for now return s.sendTo(&msg, msgLen, theAddr) == msgLen; } // just handy for quiting quickly bool IcpMsg::finish(Error err) { if (err && err != errOther) ReportError(err); return false; } bool IcpMsg::receive(Socket &s) { static IcpRawMsg msg; const Size hdrSize = SizeOf(msg) - SizeOf(msg.buf); const int len = sizeof(msg)-1; msg.buf[len] = '\0'; theSize = s.recvFrom(&msg, len, theAddr); if (theSize < 0) return finish(Error::LastExcept(EWOULDBLOCK)); if (!theSize) return finish(Error()); if (theSize < 2) return finish(errIcpMsgSize); // ntoh msg.length = ntohs(msg.length); msg.reqNum = ntohl(msg.reqNum); // we do not care about other fields for now if (msg.version != 2) // XXX: need to add v.3 for NetCAche return finish(errIcpVersion); if (theSize != (Size)msg.length || hdrSize > (Size)msg.length) return finish(errIcpMsgSize); Size urlOff = 0; bool isReply = true; switch (msg.opCode) { case icpHit: case icpMiss: case icpMissNoFetch: break; case icpQuery: urlOff += SizeOf(msg._senderHost); // skip client IP isReply = false; break; case icpError: return finish(errIcpErrOpcode); default: { if (ReportError(errIcpRepCode)) Comment << "offending opcode: " << msg.opCode << endc; return finish(errOther); } } /* pasrse payload (URL) */ msg.buf[Size(msg.length) - hdrSize] = '\0'; const char *url = msg.buf + urlOff; //XXX: we need a --dump icp option //clog << here << "XXX: ICP: got URL: " << url << endl; ReqHdr h; if (!h.parseUri(url, url + msg.length - hdrSize - urlOff, h.theUri)) return false; if (h.theUri.oid.foreignUrl()) return finish(isReply ? errIcpForeignRep : errIcpForeignReq); if (!h.theUri.host) // probably failed to parse host part return finish(errNoHostName); // XXX: the lookups below are very slow if (!TheAddrMap->has(h.theUri.host)) return finish(errForeignHostName); int viserv = -1; if (!TheHostMap->find(h.theUri.host, viserv)) return finish(errForeignHostName); h.theUri.oid.viserv(viserv); h.theUri.oid.target(-1); // unknown // copy info theOid = h.theUri.oid; theReqNum = msg.reqNum; theOpCode = (IcpOpCode) msg.opCode; return true; }