OSDN Git Service

bff67fd16a9ec82c7060ad2b5a52ff28b29bff07
[peercast-im/PeerCastIM.git] / PeerCast.root / PeerCast / core / common / gnutella.cpp
1 // ------------------------------------------------
2 // File : gnutella.cpp
3 // Date: 4-apr-2002
4 // Author: giles
5 // Desc: 
6 //              GnuPacket is a Gnutella protocol packet.
7 //              GnuStream is a Stream that reads/writes GnuPackets
8 //
9 //
10 // (c) 2002 peercast.org
11 // 
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.
17
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 // ------------------------------------------------
23
24 #include "gnutella.h"
25 #include "stream.h"
26 #include "common.h"
27 #include "servent.h"
28 #include "servmgr.h"
29 #include "stats.h"
30 #include <stdlib.h>
31 #ifdef _DEBUG
32 #include "chkMemoryLeak.h"
33 #define DEBUG_NEW new(__FILE__, __LINE__)
34 #define new DEBUG_NEW
35 #endif
36
37 // ---------------------------
38 const char *GNU_FUNC_STR(int func)
39 {
40         switch (func)
41         {
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;
48         }
49 }
50
51 // ---------------------------
52 const char *GnuStream::getRouteStr(R_TYPE r)
53 {
54         switch(r)
55         {
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;
66         }
67 }
68 // ---------------------------
69 void GnuPacket::makeChecksumID()
70 {
71         for(unsigned int i=0; i<len; i++)
72                 id.id[i%16] += data[i]; 
73 }
74
75 // ---------------------------
76 void GnuPacket::initPing(int t)
77 {
78         func = GNU_FUNC_PING;
79         ttl = t;
80         hops = 0;
81         len = 0;
82
83         id.generate();
84 }
85 // ---------------------------
86 void GnuPacket::initPong(Host &h, bool ownPong, GnuPacket &ping)
87 {
88         func = GNU_FUNC_PONG;
89         ttl = ping.hops;
90         hops = 0;
91         len = 14;
92         id = ping.id;
93
94         MemoryStream pk(data,len);
95
96         pk.writeShort(h.port);          // port
97         pk.writeLong(SWAP4(h.ip));      // ip
98         if (ownPong)
99         {
100                 pk.writeLong(chanMgr->numChannels());   // cnt
101                 pk.writeLong(servMgr->totalOutput(false));      // total
102         }else{
103                 pk.writeLong(0);                                // cnt
104                 pk.writeLong(0);                                // total
105         }
106
107
108 }
109 // ---------------------------
110 void GnuPacket::initPush(ChanHit &ch, Host &sh)
111 {
112 #if 0
113         func = GNU_FUNC_PUSH;
114         ttl = ch.numHops;
115         hops = 0;
116         len = 26;
117         id.generate();
118
119         MemoryStream data(data,len);
120
121         // ID of Hit packet
122         data.write(ch.packetID.id,16);
123
124         // index of channel
125         data.writeLong(ch.index);
126
127         data.writeLong(SWAP4(sh.ip));   // ip
128         data.writeShort(sh.port);               // port
129 #endif
130 }
131
132
133 // ---------------------------
134 bool GnuPacket::initHit(Host &h, Channel *ch, GnuPacket *query, bool push, bool busy, bool stable, bool tracker, int maxttl)
135 {
136         if (!ch)
137                 return false;
138
139         func = GNU_FUNC_HIT;
140         hops = 0;
141         id.generate();
142
143         ttl = maxttl;
144
145
146         MemoryStream mem(data,MAX_DATA);
147
148         mem.writeChar(1);                       // num hits
149         mem.writeShort(h.port);         // port
150         mem.writeLong(SWAP4(h.ip));     // ip
151
152         if (query)
153                 mem.writeLong(0);                       // speed - route
154         else
155                 mem.writeLong(1);                       // broadcast
156
157         
158         //mem.writeLong(ch->index);                             // index
159         mem.writeLong(0);                               // index
160         mem.writeShort(ch->getBitrate());       // bitrate
161         mem.writeShort(ch->localListeners());           // num listeners
162
163         mem.writeChar(0);       // no name
164
165         XML xml;
166         XML::Node *cn = ch->info.createChannelXML();
167         cn->add(ch->info.createTrackXML());
168         xml.setRoot(cn);
169         xml.writeCompact(mem);
170
171         mem.writeChar(0);                                                       // extra null 
172
173
174         // QHD
175         mem.writeLong('PCST');                          // vendor ID            
176         mem.writeChar(2);                                       // public sector length 
177
178         int f1 = 0, f2 = 0;
179
180         f1 = 1 | 4 | 8 | 32 | 64;       // use push | busy | stable | broadcast | tracker 
181
182         if (push) f2 |= 1;                      
183         if (busy) f2 |= 4;
184         if (stable) f2 |= 8;
185         if (!query) f2 |= 32;
186         if (tracker) f2 |= 64;
187         
188         mem.writeChar(f1);
189         mem.writeChar(f2);
190
191         {
192                 // write private sector
193                 char pbuf[256];
194                 MemoryStream pmem(pbuf,sizeof(pbuf));
195                 XML xml;
196                 XML::Node *pn = servMgr->createServentXML();
197                 xml.setRoot(pn);
198                 xml.writeCompact(pmem);
199                 pmem.writeChar(0);                      // add null terminator
200                 if (pmem.pos <= 255)
201                 {
202                         mem.writeChar(pmem.pos);
203                         mem.write(pmem.buf,pmem.pos);
204                 }else
205                         mem.writeChar(0);
206         }
207
208
209         // queryID/not used
210         if (query)
211                 mem.write(query->id.id,16);                                     
212         else
213                 mem.write(id.id,16);                                    
214
215         len = mem.pos;
216
217         LOG_NETWORK("Created Hit packet: %d bytes",len);
218
219         if (len >= MAX_DATA)
220                 return false;
221
222 //      servMgr->addReplyID(id);
223         return true;
224 }
225
226
227 // ---------------------------
228 void GnuPacket::initFind(const char *str, XML *xml, int maxTTL)
229 {
230
231         func = GNU_FUNC_QUERY;
232         ttl = maxTTL;
233         hops = 0;
234         id.generate();
235
236         MemoryStream mem(data,MAX_DATA);
237
238         mem.writeShort(0);              // min speed
239
240         if (str)
241         {
242                 int slen = strlen(str);
243                 mem.write((void *)str,slen+1);  // string
244         }else
245                 mem.writeChar(0);               // null string
246
247         
248         if (xml)
249                 xml->writeCompact(mem);
250
251         len = mem.pos;
252 }
253
254 // ---------------------------
255 void GnuStream::ping(int ttl)
256 {
257         GnuPacket ping;
258         ping.initPing(ttl);
259 //      servMgr->addReplyID(ping.id);
260         sendPacket(ping);
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]);
262 }
263
264 // ---------------------------
265 void GnuStream::sendPacket(GnuPacket &p)
266 {
267         try 
268         {
269                 lock.on();
270                 packetsOut++;
271                 stats.add(Stats::NUMPACKETSOUT);
272
273                 switch(p.func)
274                 {
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;
281                 }
282
283
284                 write(p.id.id,16);
285                 writeChar(p.func);      // ping func
286                 writeChar(p.ttl);       // ttl
287                 writeChar(p.hops);      // hops
288                 writeLong(p.len);       // len
289
290                 if (p.len)
291                         write(p.data,p.len);
292
293                 stats.add(Stats::PACKETDATAOUT,23+p.len);
294
295                 lock.off();
296         }catch(StreamException &e)
297         {               
298                 lock.off();
299                 throw e;
300         }
301 }
302 // ---------------------------
303 bool GnuStream::readPacket(GnuPacket &p)
304 {
305         try 
306         {
307                 lock.on();
308                 packetsIn++;
309                 stats.add(Stats::NUMPACKETSIN);
310
311                 read(p.id.id,16);
312                 p.func = readChar();
313                 p.ttl = readChar();
314                 p.hops = readChar();
315                 p.len = readLong();
316
317
318                 if ((p.hops >= 1) && (p.hops <= 10))
319                         stats.add((Stats::STAT)((int)Stats::NUMHOPS1+p.hops-1));
320
321                 stats.add(Stats::PACKETDATAIN,23+p.len);
322
323                 switch(p.func)
324                 {
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;
331                 }
332
333
334                 if (p.len)
335                 {
336                         if (p.len > GnuPacket::MAX_DATA)
337                         {
338                                 while (p.len--)
339                                         readChar();
340                                 lock.off();
341                                 return false;
342                         }
343                         read(p.data,p.len);
344                 }
345
346                 lock.off();
347                 return true;
348         }catch(StreamException &e)
349         {               
350                 lock.off();
351                 throw e;
352         }
353 }
354
355 // ---------------------------
356 GnuStream::R_TYPE GnuStream::processPacket(GnuPacket &in, Servent *serv, GnuID &routeID)
357 {
358
359         R_TYPE ret = R_DISCARD;
360
361         MemoryStream data(in.data,in.len);
362
363         Host remoteHost = serv->getHost();
364
365         in.ttl--;
366         in.hops++;
367
368         routeID = in.id;
369
370
371
372         switch(in.func)
373         {
374                 case GNU_FUNC_PING: // ping
375                         {
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]
379                                         );
380                                 Host sh = servMgr->serverHost;
381                                 if (sh.isValid())
382                                 {
383                                         if ((servMgr->getFirewall() != ServMgr::FW_ON) && (!servMgr->pubInFull()))
384                                         {
385                                                 GnuPacket pong;
386                                                 pong.initPong(sh,true,in);
387                                                 if (serv->outputPacket(pong,true))
388                                                         LOG_NETWORK("pong out");
389                                         }
390                                         ret = R_BROADCAST;                      
391                                 }
392                         }
393                         break;
394                 case GNU_FUNC_PONG: // pong
395                         {
396                                 {
397                                         int ip,port,cnt,total;
398                                         port = data.readShort();
399                                         ip = data.readLong();
400                                         cnt = data.readLong();
401                                         total = data.readLong();
402
403                                         ip = SWAP4(ip);
404
405
406                                         Host h;
407                                         h.ip = ip;
408                                         h.port = port;
409
410                                         char sIP[64],rIP[64];
411                                         h.toStr(sIP);
412                                         remoteHost.toStr(rIP);
413                                         
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]);
415
416
417                                         ret = R_DISCARD;
418
419                                         if (h.isValid())
420                                         {
421
422                                                 #if 0
423                                                 // accept if this pong is a reply from one of our own pings, otherwise route back
424                                                 if (servMgr->isReplyID(in.id))
425                                                 {
426                                                         servMgr->addHost(h,ServHost::T_SERVENT,sys->getTime());
427                                                         ret = R_ACCEPTED;
428                                                 }else
429                                                         ret = R_ROUTE;
430                                                 #endif
431                                         }
432
433                                 }
434                         }
435                         break;
436                 case GNU_FUNC_QUERY: // query
437                         ret = R_BROADCAST;
438
439                         {
440                                 Host sh = servMgr->serverHost;
441                                 if (!sh.isValid())
442                                         sh.ip = 127<<24|1;
443
444                                 char words[256];
445                                 short spd = data.readShort();
446                                 data.readString(words,sizeof(words));
447                                 words[sizeof(words)-1] = 0;
448
449                                 MemoryStream xm(&data.buf[data.pos],data.len-data.pos);
450                                 xm.buf[xm.len] = 0;
451
452                                 Channel *hits[16];
453                                 int numHits=0;
454
455                                 if (strncmp(xm.buf,"<?xml",5)==0)
456                                 {
457                                         XML xml;
458                                         xml.read(xm);
459                                         XML::Node *cn = xml.findNode("channel");
460                                         if (cn)
461                                         {
462                                                 ChanInfo info;
463                                                 info.init(cn);
464                                                 info.status = ChanInfo::S_PLAY;
465                                                 numHits = chanMgr->findChannels(info,hits,16);
466                                         }
467                                         LOG_NETWORK("query XML: %s : found %d",xm.buf,numHits);
468                                 }else{
469                                         ChanInfo info;
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);
476                                 }
477
478                         
479
480                                 for(int i=0; i<numHits; i++)
481                                 {
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();
486
487                                         GnuPacket hit;
488                                         if (hit.initHit(sh,hits[i],&in,push,busy,stable,tracker,in.hops))
489                                                 serv->outputPacket(hit,true);
490                                 }
491                         }
492                         break;
493                 case GNU_FUNC_PUSH:     // push
494                         {
495
496                                 GnuID pid;
497                                 data.read(pid.id,16);
498                                 
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]);
501
502
503                                 int index = data.readLong();
504                                 int ip = data.readLong();
505                                 int port = data.readShort();
506
507                         
508                                 ip = SWAP4(ip);
509
510                                 Host h(ip,port);
511                                 char hostName[64];
512                                 h.toStr(hostName);
513
514 #if 0
515                                 if (servMgr->isReplyID(pid))
516                                 {
517 #if 0
518                                         Channel *c = chanMgr->findChannelByIndex(index);
519
520                                         if (!c)
521                                         {
522                                                 LOG_NETWORK("push 0x%x to %s: Not found",index,hostName);
523                                         }else
524                                         {
525                                                 if (!c->isFull() && !servMgr->streamFull())
526                                                 {
527                                                         LOG_NETWORK("push: 0x%x to %s: OK",index,hostName);
528
529                                                         Servent *s = servMgr->allocServent();
530                                                         if (s)
531                                                                 s->initGIV(h,c->info.id);
532                                                 }else
533                                                         LOG_NETWORK("push: 0x%x to %s: FULL",index,hostName);
534                                         }
535 #endif
536                                         ret = R_ACCEPTED;
537                                 }else{
538                                         LOG_NETWORK("push: 0x%x to %s: ROUTE",index,hostName);
539                                         routeID = pid;
540                                         ret = R_ROUTE;
541                                 }
542 #endif          
543                         }
544                         break;
545                 case GNU_FUNC_HIT: // hit
546                         {
547                                 ret = R_DISCARD;
548
549                                 ChanHit hit;
550                                 if (readHit(data,hit,in.hops,in.id))
551                                 {
552
553                                         char flstr[64];
554                                         flstr[0]=0;
555                                         if (hit.firewalled) strcat(flstr,"Push,");
556                                         if (hit.tracker) strcat(flstr,"Tracker,");
557                                         
558 #if 0
559                                         if ((spd == 0) && (!isBroadcastHit))
560                                         {
561                                                 if (servMgr->isReplyID(queryID))
562                                                 {
563                                                         ret = R_ACCEPTED;
564                                                         LOG_NETWORK("self-hit: %s 0x%02x %s %d chan",hostName,f2,flstr,num);
565                                                 }else
566                                                 {
567                                                         routeID = queryID;
568                                                         ret = R_ROUTE;
569                                                         LOG_NETWORK("route-hit: %s 0x%02x %s %d chan",hostName,f2,flstr,num);
570                                                 }
571                                         }else
572                                         {
573                                                 ret = R_BROADCAST;
574                                                 LOG_NETWORK("broadcast-hit: %s 0x%02x %s %d chan",hostName,f2,flstr,num);
575                                         }
576 #else
577                                         ret = R_BROADCAST;
578                                         LOG_NETWORK("broadcast-hit: %s",flstr);
579 #endif
580                                 }
581                         }
582                         break;
583                 default:
584                         LOG_NETWORK("packet: %d",in.func);
585                         break;
586         }
587
588         
589         if ((in.ttl > 10) || (in.hops > 10) || (in.ttl==0))
590                 if ((ret == R_BROADCAST) || (ret == R_ROUTE))
591                         ret = R_DEAD;
592
593         return ret;
594 }
595
596 // ---------------------------
597 bool GnuStream::readHit(Stream &data, ChanHit &ch,int hops,GnuID &id)
598 {
599         int i;
600         int num = data.readChar();      // hits
601         int port = data.readShort();            // port
602         int ip = data.readLong();               // ip
603         ip = SWAP4(ip);
604         int spd = data.readLong();              // speed/broadcast
605
606         Host h(ip,port);
607         char hostName[64];
608
609         h.IPtoStr(hostName);
610
611         bool dataValid=true;
612
613         ChanHit *hits[100];
614         int numHits=0;
615
616         for(i=0; i<num; i++)
617         {
618                 int index,bitrate,listeners;
619
620
621                 index = data.readLong();                // index
622                 bitrate = data.readShort();             // bitrate
623                 listeners = data.readShort();   // listeners
624
625                 // read name .. not used.
626                 char fname[256];
627                 data.readString(fname,sizeof(fname));
628                 fname[sizeof(fname)-1] = 0;
629
630                 ch.init();
631                 ch.firewalled = false;          // default to NO as we dont get the info until the next section.
632                 ch.host = h;
633                 ch.numListeners = listeners;
634                 ch.numHops = hops;
635                 ch.rhost[0] = ch.host;
636
637                 ChanInfo info;
638
639
640                 {
641                         char xmlData[4000];
642                         int xlen = data.readString(xmlData,sizeof(xmlData));
643
644                         if ((strncmp(xmlData,"<?xml",5)==0) && (xlen < GnuPacket::MAX_DATA))
645                         {
646                                 //LOG_NETWORK("Hit XML: %s",xmlData);
647
648                                 MemoryStream xm(xmlData,xlen);
649                                 XML xml;
650                                 xml.read(xm);
651                                 XML::Node *n = xml.findNode("channel");
652                                 if (n)
653                                 {
654                                         info.init(n);
655                                         char idStr[64];
656                                         info.id.toStr(idStr);
657                                         LOG_NETWORK("got hit %s %s",idStr,info.name.cstr());
658
659                                         ch.upTime = n->findAttrInt("uptime");
660
661                                 }else
662                                         LOG_NETWORK("Missing Channel node");
663                         }else
664                         {
665                                 LOG_NETWORK("Missing XML data");
666                                 //LOG_NETWORK("%s",xmlData);
667                                 dataValid = false;
668                         }
669                 }
670
671                 if (info.id.isSet())
672                 {
673                         if (!chanMgr->findHitList(info))
674                                 chanMgr->addHitList(info);
675
676                         ch.recv = true;
677                         ch.chanID = info.id;
678                         ChanHit *chp = chanMgr->addHit(ch);             
679
680                         if ((chp) && (numHits<100))
681                                 hits[numHits++] = chp;
682                 }
683
684         }
685
686
687         int vendor = data.readLong();   // vendor ID
688
689         int pubLen = data.readChar();   // public sec length - should be 2
690
691         int f1 = data.readChar() & 0xff; // flags 1
692         int f2 = data.readChar() & 0xff; // flags 2
693
694         pubLen -= 2;
695         while (pubLen-->0)
696                 data.readChar();
697
698
699         char agentStr[16];
700         agentStr[0]=0;
701         int maxPreviewTime=0;
702
703         // read private sector with peercast servant specific info
704         int privLen = data.readChar();  
705
706         if (privLen)
707         {
708                 char privData[256];
709                 data.read(privData,privLen);
710                 if (strncmp(privData,"<?xml",5)==0)
711                 {
712                         MemoryStream xm(privData,privLen);
713                         XML xml;
714                         xml.read(xm);
715                         XML::Node *sn = xml.findNode("servent");
716                         if (sn)
717                         {
718                                 char *ag = sn->findAttr("agent");
719                                 if (ag)
720                                 {
721                                         strncpy(agentStr,ag,16);
722                                         agentStr[15]=0;
723                                 }
724                                 maxPreviewTime = sn->findAttrInt("preview");
725                         }
726
727                 }
728         }
729
730
731         // not used anymore
732         GnuID queryID;
733         data.read(queryID.id,16);
734
735         bool isBroadcastHit=false;
736         if (f1 & 32)
737                 isBroadcastHit = (f2 & 32)!=0;
738
739         for(i=0; i<numHits; i++)
740         {
741                 if (f1 & 1)
742                         hits[i]->firewalled = (f2 & 1)!=0;
743
744                 if (f1 & 64)
745                         hits[i]->tracker = (f2 & 64)!=0;
746
747         }
748
749         return dataValid;
750 }
751