1 // ------------------------------------------------
6 // GnuPacket is a Gnutella protocol packet.
7 // GnuStream is a Stream that reads/writes GnuPackets
10 // (c) 2002 peercast.org
12 // ------------------------------------------------
13 // This program is free software; you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation; either version 2 of the License, or
16 // (at your option) any later version.
18 // This program is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU General Public License for more details.
22 // ------------------------------------------------
32 #include "chkMemoryLeak.h"
33 #define DEBUG_NEW new(__FILE__, __LINE__)
37 // ---------------------------
38 const char *GNU_FUNC_STR(int func)
42 case GNU_FUNC_PING: return "PING"; break;
43 case GNU_FUNC_PONG: return "PONG"; break;
44 case GNU_FUNC_QUERY: return "QUERY"; break;
45 case GNU_FUNC_HIT: return "HIT"; break;
46 case GNU_FUNC_PUSH: return "PUSH"; break;
47 default: return "UNKNOWN"; break;
51 // ---------------------------
52 const char *GnuStream::getRouteStr(R_TYPE r)
56 case R_PROCESS: return "PROCESS"; break;
57 case R_DEAD: return "DEAD"; break;
58 case R_DISCARD: return "DISCARD"; break;
59 case R_ACCEPTED: return "ACCEPTED"; break;
60 case R_BROADCAST: return "BROADCAST"; break;
61 case R_ROUTE: return "ROUTE"; break;
62 case R_DUPLICATE: return "DUPLICATE"; break;
63 case R_BADVERSION: return "BADVERSION"; break;
64 case R_DROP: return "DROP"; break;
65 default: return "UNKNOWN"; break;
68 // ---------------------------
69 void GnuPacket::makeChecksumID()
71 for(unsigned int i=0; i<len; i++)
72 id.id[i%16] += data[i];
75 // ---------------------------
76 void GnuPacket::initPing(int t)
85 // ---------------------------
86 void GnuPacket::initPong(Host &h, bool ownPong, GnuPacket &ping)
94 MemoryStream pk(data,len);
96 pk.writeShort(h.port); // port
97 pk.writeLong(SWAP4(h.ip)); // ip
100 pk.writeLong(chanMgr->numChannels()); // cnt
101 pk.writeLong(servMgr->totalOutput(false)); // total
103 pk.writeLong(0); // cnt
104 pk.writeLong(0); // total
109 // ---------------------------
110 void GnuPacket::initPush(ChanHit &ch, Host &sh)
113 func = GNU_FUNC_PUSH;
119 MemoryStream data(data,len);
122 data.write(ch.packetID.id,16);
125 data.writeLong(ch.index);
127 data.writeLong(SWAP4(sh.ip)); // ip
128 data.writeShort(sh.port); // port
133 // ---------------------------
134 bool GnuPacket::initHit(Host &h, Channel *ch, GnuPacket *query, bool push, bool busy, bool stable, bool tracker, int maxttl)
146 MemoryStream mem(data,MAX_DATA);
148 mem.writeChar(1); // num hits
149 mem.writeShort(h.port); // port
150 mem.writeLong(SWAP4(h.ip)); // ip
153 mem.writeLong(0); // speed - route
155 mem.writeLong(1); // broadcast
158 //mem.writeLong(ch->index); // index
159 mem.writeLong(0); // index
160 mem.writeShort(ch->getBitrate()); // bitrate
161 mem.writeShort(ch->localListeners()); // num listeners
163 mem.writeChar(0); // no name
166 XML::Node *cn = ch->info.createChannelXML();
167 cn->add(ch->info.createTrackXML());
169 xml.writeCompact(mem);
171 mem.writeChar(0); // extra null
175 mem.writeLong('PCST'); // vendor ID
176 mem.writeChar(2); // public sector length
180 f1 = 1 | 4 | 8 | 32 | 64; // use push | busy | stable | broadcast | tracker
185 if (!query) f2 |= 32;
186 if (tracker) f2 |= 64;
192 // write private sector
194 MemoryStream pmem(pbuf,sizeof(pbuf));
196 XML::Node *pn = servMgr->createServentXML();
198 xml.writeCompact(pmem);
199 pmem.writeChar(0); // add null terminator
202 mem.writeChar(pmem.pos);
203 mem.write(pmem.buf,pmem.pos);
211 mem.write(query->id.id,16);
217 LOG_NETWORK("Created Hit packet: %d bytes",len);
222 // servMgr->addReplyID(id);
227 // ---------------------------
228 void GnuPacket::initFind(const char *str, XML *xml, int maxTTL)
231 func = GNU_FUNC_QUERY;
236 MemoryStream mem(data,MAX_DATA);
238 mem.writeShort(0); // min speed
242 size_t slen = strlen(str);
243 mem.write((void *)str,slen+1); // string
245 mem.writeChar(0); // null string
249 xml->writeCompact(mem);
254 // ---------------------------
255 void GnuStream::ping(int ttl)
259 // servMgr->addReplyID(ping.id);
261 LOG_NETWORK("ping out %02x%02x%02x%02x",ping.id.id[0],ping.id.id[1],ping.id.id[2],ping.id.id[3]);
264 // ---------------------------
265 void GnuStream::sendPacket(GnuPacket &p)
271 stats.add(Stats::NUMPACKETSOUT);
275 case GNU_FUNC_PING: stats.add(Stats::NUMPINGOUT); break;
276 case GNU_FUNC_PONG: stats.add(Stats::NUMPONGOUT); break;
277 case GNU_FUNC_QUERY: stats.add(Stats::NUMQUERYOUT); break;
278 case GNU_FUNC_HIT: stats.add(Stats::NUMHITOUT); break;
279 case GNU_FUNC_PUSH: stats.add(Stats::NUMPUSHOUT); break;
280 default: stats.add(Stats::NUMOTHEROUT); break;
285 writeChar(p.func); // ping func
286 writeChar(p.ttl); // ttl
287 writeChar(p.hops); // hops
288 writeLong(p.len); // len
293 stats.add(Stats::PACKETDATAOUT,23+p.len);
296 }catch(StreamException &e)
302 // ---------------------------
303 bool GnuStream::readPacket(GnuPacket &p)
309 stats.add(Stats::NUMPACKETSIN);
318 if ((p.hops >= 1) && (p.hops <= 10))
319 stats.add((Stats::STAT)((int)Stats::NUMHOPS1+p.hops-1));
321 stats.add(Stats::PACKETDATAIN,23+p.len);
325 case GNU_FUNC_PING: stats.add(Stats::NUMPINGIN); break;
326 case GNU_FUNC_PONG: stats.add(Stats::NUMPONGIN); break;
327 case GNU_FUNC_QUERY: stats.add(Stats::NUMQUERYIN); break;
328 case GNU_FUNC_HIT: stats.add(Stats::NUMHITIN); break;
329 case GNU_FUNC_PUSH: stats.add(Stats::NUMPUSHIN); break;
330 default: stats.add(Stats::NUMOTHERIN); break;
336 if (p.len > GnuPacket::MAX_DATA)
348 }catch(StreamException &e)
355 // ---------------------------
356 GnuStream::R_TYPE GnuStream::processPacket(GnuPacket &in, Servent *serv, GnuID &routeID)
359 R_TYPE ret = R_DISCARD;
361 MemoryStream data(in.data,in.len);
363 Host remoteHost = serv->getHost();
374 case GNU_FUNC_PING: // ping
376 LOG_NETWORK("ping: from %d.%d.%d.%d : %02x%02x%02x%02x",
377 remoteHost.ip>>24&0xff,remoteHost.ip>>16&0xff,remoteHost.ip>>8&0xff,remoteHost.ip&0xff,
378 in.id.id[0],in.id.id[1],in.id.id[2],in.id.id[3]
380 Host sh = servMgr->serverHost;
383 if ((servMgr->getFirewall() != ServMgr::FW_ON) && (!servMgr->pubInFull()))
386 pong.initPong(sh,true,in);
387 if (serv->outputPacket(pong,true))
388 LOG_NETWORK("pong out");
394 case GNU_FUNC_PONG: // pong
397 int ip,port,cnt,total;
398 port = data.readShort();
399 ip = data.readLong();
400 cnt = data.readLong();
401 total = data.readLong();
410 char sIP[64],rIP[64];
412 remoteHost.toStr(rIP);
414 LOG_NETWORK("pong: %s via %s : %02x%02x%02x%02x",sIP,ip,rIP,in.id.id[0],in.id.id[1],in.id.id[2],in.id.id[3]);
423 // accept if this pong is a reply from one of our own pings, otherwise route back
424 if (servMgr->isReplyID(in.id))
426 servMgr->addHost(h,ServHost::T_SERVENT,sys->getTime());
436 case GNU_FUNC_QUERY: // query
440 Host sh = servMgr->serverHost;
445 short spd = data.readShort();
446 data.readString(words,sizeof(words));
447 words[sizeof(words)-1] = 0;
449 MemoryStream xm(&data.buf[data.pos],data.len-data.pos);
455 if (strncmp(xm.buf,"<?xml",5)==0)
459 XML::Node *cn = xml.findNode("channel");
464 info.status = ChanInfo::S_PLAY;
465 numHits = chanMgr->findChannels(info,hits,16);
467 LOG_NETWORK("query XML: %s : found %d",xm.buf,numHits);
470 info.name.set(words);
471 info.genre.set(words);
472 info.id.fromStr(words);
473 info.status = ChanInfo::S_PLAY;
474 numHits = chanMgr->findChannels(info,hits,16);
475 LOG_NETWORK("query STR: %s : found %d",words,numHits);
480 for(int i=0; i<numHits; i++)
482 bool push = (servMgr->getFirewall()!=ServMgr::FW_OFF);
483 bool busy = (servMgr->pubInFull() && servMgr->outFull()) || servMgr->relaysFull();
484 bool stable = servMgr->totalStreams>0;
485 bool tracker = hits[i]->isBroadcasting();
488 if (hit.initHit(sh,hits[i],&in,push,busy,stable,tracker,in.hops))
489 serv->outputPacket(hit,true);
493 case GNU_FUNC_PUSH: // push
497 data.read(pid.id,16);
499 //LOG("push serv= %02x%02x%02x%02x",servMgr->id[0],servMgr->id[1],servMgr->id[2],servMgr->id[3]);
500 //LOG("pack = %02x%02x%02x%02x",id[0],id[1],id[2],id[3]);
503 int index = data.readLong();
504 int ip = data.readLong();
505 int port = data.readShort();
515 if (servMgr->isReplyID(pid))
518 Channel *c = chanMgr->findChannelByIndex(index);
522 LOG_NETWORK("push 0x%x to %s: Not found",index,hostName);
525 if (!c->isFull() && !servMgr->streamFull())
527 LOG_NETWORK("push: 0x%x to %s: OK",index,hostName);
529 Servent *s = servMgr->allocServent();
531 s->initGIV(h,c->info.id);
533 LOG_NETWORK("push: 0x%x to %s: FULL",index,hostName);
538 LOG_NETWORK("push: 0x%x to %s: ROUTE",index,hostName);
545 case GNU_FUNC_HIT: // hit
550 if (readHit(data,hit,in.hops,in.id))
555 if (hit.firewalled) strcat(flstr,"Push,");
556 if (hit.tracker) strcat(flstr,"Tracker,");
559 if ((spd == 0) && (!isBroadcastHit))
561 if (servMgr->isReplyID(queryID))
564 LOG_NETWORK("self-hit: %s 0x%02x %s %d chan",hostName,f2,flstr,num);
569 LOG_NETWORK("route-hit: %s 0x%02x %s %d chan",hostName,f2,flstr,num);
574 LOG_NETWORK("broadcast-hit: %s 0x%02x %s %d chan",hostName,f2,flstr,num);
578 LOG_NETWORK("broadcast-hit: %s",flstr);
584 LOG_NETWORK("packet: %d",in.func);
589 if ((in.ttl > 10) || (in.hops > 10) || (in.ttl==0))
590 if ((ret == R_BROADCAST) || (ret == R_ROUTE))
596 // ---------------------------
597 bool GnuStream::readHit(Stream &data, ChanHit &ch,int hops,GnuID &id)
600 int num = data.readChar(); // hits
601 int port = data.readShort(); // port
602 int ip = data.readLong(); // ip
604 int spd = data.readLong(); // speed/broadcast
618 int index,bitrate,listeners;
621 index = data.readLong(); // index
622 bitrate = data.readShort(); // bitrate
623 listeners = data.readShort(); // listeners
625 // read name .. not used.
627 data.readString(fname,sizeof(fname));
628 fname[sizeof(fname)-1] = 0;
631 ch.firewalled = false; // default to NO as we dont get the info until the next section.
633 ch.numListeners = listeners;
635 ch.rhost[0] = ch.host;
642 int xlen = data.readString(xmlData,sizeof(xmlData));
644 if ((strncmp(xmlData,"<?xml",5)==0) && (xlen < GnuPacket::MAX_DATA))
646 //LOG_NETWORK("Hit XML: %s",xmlData);
648 MemoryStream xm(xmlData,xlen);
651 XML::Node *n = xml.findNode("channel");
656 info.id.toStr(idStr);
657 LOG_NETWORK("got hit %s %s",idStr,info.name.cstr());
659 ch.upTime = n->findAttrInt("uptime");
662 LOG_NETWORK("Missing Channel node");
665 LOG_NETWORK("Missing XML data");
666 //LOG_NETWORK("%s",xmlData);
673 if (!chanMgr->findHitList(info))
674 chanMgr->addHitList(info);
678 ChanHit *chp = chanMgr->addHit(ch);
680 if ((chp) && (numHits<100))
681 hits[numHits++] = chp;
687 int vendor = data.readLong(); // vendor ID
689 int pubLen = data.readChar(); // public sec length - should be 2
691 int f1 = data.readChar() & 0xff; // flags 1
692 int f2 = data.readChar() & 0xff; // flags 2
701 int maxPreviewTime=0;
703 // read private sector with peercast servant specific info
704 int privLen = data.readChar();
709 data.read(privData,privLen);
710 if (strncmp(privData,"<?xml",5)==0)
712 MemoryStream xm(privData,privLen);
715 XML::Node *sn = xml.findNode("servent");
718 char *ag = sn->findAttr("agent");
721 strncpy(agentStr,ag,16);
724 maxPreviewTime = sn->findAttrInt("preview");
733 data.read(queryID.id,16);
735 bool isBroadcastHit=false;
737 isBroadcastHit = (f2 & 32)!=0;
739 for(i=0; i<numHits; i++)
742 hits[i]->firewalled = (f2 & 1)!=0;
745 hits[i]->tracker = (f2 & 64)!=0;