]> icculus.org git repositories - btb/d2x.git/blob - main/ip_base.cpp
Lotsa networking stuff from d1x
[btb/d2x.git] / main / ip_base.cpp
1 /*
2  * $Source: /cvs/cvsroot/d2x/main/ip_base.cpp,v $
3  * $Revision: 1.2 $
4  * $Author: bradleyb $
5  * $Date: 2002-02-13 10:39:21 $
6  *
7  * ip_base.cpp - base for NAT-compatible udp/ip code.
8  * added 2000/02/07 Matt Mueller
9  *
10  * $Log: not supported by cvs2svn $
11  * Revision 1.1  2002/02/06 09:22:41  bradleyb
12  * Adding d1x network code
13  *
14  *
15  */
16
17 #ifdef HAVE_CONFIG_H
18 #include <conf.h>
19 #endif
20
21 extern "C" {
22 #include "timer.h"
23 #include "mono.h"
24 #include "args.h"
25 }
26 #include <string.h>
27 #include "ip_base.h"
28
29 int myport=-1;
30
31 int baseport=UDP_BASEPORT;
32
33 ip_addr_list ip_my_addrs;
34 ip_peer_list peer_list;
35
36 int ip_handshake_base::fillbuf(ubyte *buf){
37         u_int32_t tmp32;
38         u_int16_t tmp16;
39         int pos=0;
40         buf[pos++]=type;
41         buf[pos++]=state;
42 //      memcpy(buf+pos,id,6);pos+=6;
43         pos+=id.fillbuf(buf+pos);
44         tmp16=htons(iver);
45         memcpy(buf+pos,&tmp16,2);pos+=2;
46         tmp32=htonl(tryid);
47         memcpy(buf+pos,&tmp32,4);pos+=4;
48         return pos;
49 }
50 int ip_handshake_base::readbuf(ubyte *buf){
51         int pos=0;
52         nopend=1;
53         type=buf[pos++];
54         state=buf[pos++];
55 //      memcpy(id,buf+pos,6);pos+=6;
56         pos+=id.readbuf(buf+pos);
57         memcpy(&iver,buf+pos,2);pos+=2;
58         iver=htons(iver);
59         memcpy(&tryid,buf+pos,4);pos+=4;
60         tryid=htonl(tryid);
61         return pos;
62 }
63 void ip_handshake_base::setstate(int newstate){
64         if (!nopend){
65                 if (state&STATE_NEED_RESEND){
66                         if (!(newstate&STATE_NEED_RESEND))
67                                 peer_list.pendinghandshakes--;
68                 }else{
69                         if (newstate&STATE_NEED_RESEND)
70                                 peer_list.pendinghandshakes++;
71                 }
72                 mprintf((0,"peer_list.pendinghandshakes=%i\n",peer_list.pendinghandshakes));
73         }
74         state=newstate;attempts=0;nextsend=0;
75 }
76 int ip_handshake_base::addstate(int newstate){
77         if (state & STATE_ERR){
78                 setstate((state | newstate) & ~STATE_ERR);
79                 return 2;
80         }
81         if ((state | newstate) != state){
82                 setstate(state | newstate);
83                 return 1;
84         }
85         return 0;
86 }
87
88 int ip_handshake_info::fillbuf(ubyte *buf){
89         int pos=ip_handshake_base::fillbuf(buf);
90         pos+=addr.fillbuf(buf+pos);
91 //      memcpy(buf+pos,addr,6);pos+=6;
92         return pos;
93 }
94 int ip_handshake_info::readbuf(ubyte *buf){
95         int pos=ip_handshake_base::readbuf(buf);
96         pos+=addr.readbuf(buf+pos);
97 //      memcpy(addr,buf+pos,6);pos+=6;
98         return pos;
99 }
100
101 int ip_handshake_relay::fillbuf(ubyte *buf){
102         u_int16_t tmp16;
103         int pos=ip_handshake_base::fillbuf(buf);
104 //      memcpy(buf+pos,r_addr[0],6);pos+=6;
105 //      memcpy(buf+pos,r_addr[1],6);pos+=6;
106 //      memcpy(buf+pos,r_id,6);pos+=6;
107         pos+=r_id.fillbuf(buf+pos);
108         tmp16=htons(r_iver);
109         memcpy(buf+pos,&tmp16,2);pos+=2;
110         pos+=r_addr.fillbuf(buf+pos);
111 //      pos+=r_addr[1].fillbuf(buf+pos);
112         return pos;
113 }
114 int ip_handshake_relay::readbuf(ubyte *buf){
115         int pos=ip_handshake_base::readbuf(buf);
116 //      memcpy(r_addr[0],buf+pos,6);pos+=6;
117 //      memcpy(r_addr[1],buf+pos,6);pos+=6;
118 //      memcpy(r_id,buf+pos,6);pos+=6;
119         pos+=r_id.readbuf(buf+pos);
120         memcpy(&r_iver,buf+pos,2);pos+=2;
121         r_iver=htons(r_iver);
122         pos+=r_addr.readbuf(buf+pos);
123 //      pos+=r_addr[1].readbuf(buf+pos);
124         return pos;
125 }
126 ip_handshake_relay::ip_handshake_relay(ip_peer *torelay):ip_handshake_base(1){
127         type=IP_CFG_RELAY;
128         r_id=torelay->id;
129         r_iver=torelay->iver;
130         r_addr=torelay->addr;
131 //      r_addr[1]=torelay->addr[1];
132         setstate(STATE_INEEDINFO);
133 }
134
135 /*void ip_peer::set_good_addr(ubyte *fraddr){
136         int i;
137         for (i=0;i<numaddr;i++)
138                 if (addr[i]==fraddr){
139                         goodaddr=i;
140                         return;
141                 }
142         //######hrm.
143         addr[1]=fraddr;
144         numaddr=2;
145         goodaddr=1;
146 }*/
147 void ip_peer::send_handshake(ip_handshake_base*hsb){
148         ubyte buf[256];
149         int s=0;
150         memcpy(buf+s,DXXcfgid,4);s+=4;
151         s+=hsb->fillbuf(buf+s);
152         assert(s<256);
153         if (addr.goodaddr==NULL){
154                 ip_addr_list::iterator i;
155                 for (i=addr.begin();i!=addr.end();i++)
156                         ip_sendtoca(*i,buf,s);
157 //              int i;
158 //              for (i=0;i<addr.naddr;i++)
159 //                      ip_sendtoca(addr[i],buf,s);
160         }else
161                 //ip_sendtoca(addr[goodaddr],buf,s);
162                 ip_sendtoca(*addr.goodaddr,buf,s);
163         hsb->nextsend=timer_get_approx_seconds()+IP_HS_RETRYTIME;
164         hsb->attempts++;
165 /*#ifdef UDPDEBUG
166         {
167                 int hj;
168                 printf(MSGHDR"sending handshake %i (t%i state %i(%s)) for (%i)",hsb->attempts,hsb->type,hsb->state,ip_hs_statetoa(hsb->state),addr.goodaddr);
169                 for (hj=0;hj<numaddr;hj++){
170                         if (hj>0)
171                                 printf(", ");
172                         dumpraddr(addr[hj].addr);
173                 }
174                 printf(" v%i\n",iver);
175         }
176 #endif*/
177 }
178 bool ip_peer::verify_addr(ip_addr_list &fraddrs){
179         int a1=addr.add(fraddrs);
180         if (a1)
181                 addr.goodaddr=NULL;
182 /*      a1=(addr[0]!=fraddr0 && addr[1]!=fraddr0);
183         a2=(addr[0]!=fraddr1 && addr[1]!=fraddr1);
184         if (a1 || a2 || (numaddr<2 && fraddr0!=fraddr1))
185         {
186                 mprintf((0,"verify_peer_addr_hsi %i %i\n",a1,a2));
187                 addr[0]=fraddr1;
188                 addr[1]=fraddr0;
189                 goodaddr=-1;
190                 numaddr=2;
191                 return 1;
192         }*/
193         return a1>0;
194 }
195 ip_peer * ip_peer_list::add_1(ip_addr addr/*,ip_id id*/){
196         ip_peer*n=add_unk();
197         n->addr.add(addr);
198 //      n->goodaddr=-1;
199 //      n->numaddr=1;
200 //      n->id=id;
201         return n;
202 }
203 ip_peer * ip_peer_list::add_full(ip_id id, u_int16_t iver,ip_addr_list &addrs){
204         ip_peer*n=add_id(id);
205 /*      n->addr[0]=addr0;
206         n->addr[1]=addr1;
207         n->goodaddr=-1;
208         if (addr0==addr1){
209                 n->numaddr=1;
210         }else{
211                 n->numaddr=2;
212         }*/
213         n->addr.add(addrs);
214         n->iver=iver;
215         mprintf((0,"addfull %i addrs\n",n->addr.naddr));
216 //      n->id=id;
217         return n;
218 }
219 void ip_peer_list::make_full(ip_peer*p,ip_id id, u_int16_t iver, ip_addr_list &addrs){
220         list<ip_peer*>::iterator unki=find(unknown_peers.begin(),unknown_peers.end(),p);
221         if (unki!=unknown_peers.end())
222                 unknown_peers.erase(unki);
223         p->id=id;
224         p->iver=iver;
225 //      p->addr[0]=addr0;
226 //      p->addr[1]=addr1;
227         p->addr.add(addrs);
228         peers.insert(peer_map::value_type(p->id,p));
229 }
230 ip_peer * ip_peer_list::find_byid(ip_id id){
231         peer_map::iterator i=peers.find(id);
232         if (i!=peers.end())
233                 return (*i).second;
234         return NULL;
235 }
236 struct do_find_by_addr : public unary_function<ip_peer*,bool> {
237         int j;
238         ip_addr addr;
239         bool operator()(ip_peer * p) {
240 ////    bool operator()(pair<const ip_id,ip_peer *> &) {
241 //              for(j=0;j<p->numaddr;j++)
242 //                      if (addr==p->addr[j])
243 //                              return 1;
244                 if (p->addr.hasaddr(addr))
245                         return 1;
246                 return 0;
247         }
248 };
249 template <class ret,class p1,class p2, class fo>
250 struct pairkiller {
251         fo *o;
252         ret operator()(pair<p1,p2> &aoeuaoeu) {
253                 return (*o)(aoeuaoeu.second);
254         }
255         pairkiller(fo *no):o(no){}
256 };
257 //template <class ret,class p1,class p2, class fo>
258 template <class p1, class fo>
259 pairkiller<typename fo::result_type,p1,typename fo::argument_type,fo> pairkill(fo * no){
260         return pairkiller<typename fo::result_type,p1,typename fo::argument_type,fo>(no);
261 }
262
263 ip_peer * ip_peer_list::find_by_addr(ip_addr addr){
264         do_find_by_addr fba;
265 //      pairkiller<const ip_id,ip_peer*,do_find_by_addr> fbap(&fba);
266         fba.addr=addr;
267         peer_map::iterator i=find_if(peers.begin(),peers.end(),pairkill<const ip_id>(&fba));
268         if (i!=peers.end())
269                 return (*i).second;
270         list<ip_peer*>::iterator j=find_if(unknown_peers.begin(),unknown_peers.end(),fba);
271         if (j!=unknown_peers.end())
272                 return (*j);
273         return NULL;
274 }
275 ip_peer * ip_peer_list::find_unk_by_addr(ip_addr addr){
276         do_find_by_addr fba;
277         fba.addr=addr;
278         list<ip_peer*>::iterator j=find_if(unknown_peers.begin(),unknown_peers.end(),fba);
279         if (j!=unknown_peers.end())
280                 return (*j);
281         return NULL;
282 }
283 struct do_peer_handshake : public unary_function<ip_peer *, void>{
284         fix mintime;
285         list<ip_handshake_base*>::iterator i;
286         ip_handshake_base *hsb;
287         void operator() (ip_peer* peer) {
288                 for(i=peer->handshakes.begin();i!=peer->handshakes.end();++i){
289                         hsb=(*i);
290                         //if (hsb->state<IPNOSTATE && hsb->nextsend<mintime){
291                         if ((hsb->state & STATE_NEED_RESEND) && hsb->nextsend<mintime){
292                                 if(hsb->attempts>IP_MAX_HS_ATTEMPTS){
293 /*#ifdef UDPDEBUG
294                                         int hj;
295                                         printf(MSGHDR"handshake timeout (state %i(%s)) for (%i)",hsb->state,ip_hs_statetoa(hsb->state),peer->goodaddr);
296                                         for (hj=0;hj<peer->numaddr;hj++){
297                                                 if (hj>0)
298                                                         printf(", ");
299                                                 dumpraddr(peer->addr[hj].addr);
300                                         }
301                                         printf(" v%i\n",peer->iver);
302 #endif*/
303                                         hsb->setstate(STATE_ERR);
304                                 }else{
305                         //                                      handshake_buf.state=handshakers[i].state;
306                                         peer->send_handshake(hsb);
307                                 }
308                         }
309                 }
310         }
311 };
312 void ip_peer_list::handshake_frame(void){
313         if(pendinghandshakes){
314                 fix mintime=timer_get_approx_seconds();//try every X seconds
315                 if (pendinghandshake_lasttime<mintime-IP_HS_FRAME_RETRYTIME){
316                         do_peer_handshake doph;
317 //                      pairkiller<const ip_id,ip_peer*,do_peer_handshake> dophp(&doph);
318                         doph.mintime=mintime;
319                         for_each(peers.begin(),peers.end(),pairkill<const ip_id>(&doph));
320                         for_each(unknown_peers.begin(),unknown_peers.end(),doph);
321 //                      int i;
322 //                      mintime-=IP_HS_RETRYTIME;//try every X seconds
323 /*                      for (i=0;i<numpeers;i++){
324                                 ip_handshake_do_hs(&peers[i],&peers[i].query,mintime);
325                                 ip_handshake_do_hs(&peers[i],&peers[i].reply,mintime);
326                         }*/
327                 }
328         }
329 }
330 struct do_peer_delete : public unary_function<ip_peer *, void>{
331         void operator() (ip_peer* peer) {
332                 delete peer;
333         }
334 };
335 void ip_peer_list::clear(void){
336         do_peer_delete dopd;
337         for_each(peers.begin(),peers.end(),pairkill<const ip_id>(&dopd));
338         peers.erase(peers.begin(),peers.end());
339         for_each(unknown_peers.begin(),unknown_peers.end(),dopd);
340         unknown_peers.erase(unknown_peers.begin(),unknown_peers.end());
341 }
342 ip_peer_list::~ip_peer_list(){
343         clear();
344 }
345
346
347 #ifdef UDPDEBUG
348 //000a0oeuaoeu
349
350 static void dumpid(unsigned char *a)
351 {
352         printf("<%u.%u.%u.%u.%u.%u>",a[0],a[1],a[2],a[3],a[4],a[5]);
353 }
354 #endif
355
356 int ip_sendtoid(ubyte *id,const void *buf,int len){
357 //      ip_handshaker *hsr=find_handshaker_by_id(id);
358         ip_peer *p=peer_list.find_byid(id);
359         if (!p || p->addr.goodaddr==NULL){
360 #ifdef UDPDEBUG
361                 printf(MSGHDR"send to invalid id(");
362                 dumpid(id);
363                 printf(") %p.",p);
364 #endif
365                 return -1;
366         }
367         return ip_sendtoca(*p->addr.goodaddr,buf,len);
368 }
369
370 void ip_receive_cfg(ubyte *buf,int buflen,ip_addr fromaddr){
371         ip_peer *p;
372         switch(buf[0]){
373                 case IP_CFG_SORRY:
374                         {
375 #ifdef UDPDEBUG
376         printf("ip_receive_cfg: %i %i ",buf[0],buf[1]);
377         dumprid(buf+2);
378         printf(" v%i tryid%u\n",ntohs(*(unsigned short*)(buf+8)),(unsigned int)ntohl(*(u_int32_t*)(buf+10)));
379 #endif
380
381                         }break;
382                 case IP_CFG_HANDSHAKE:
383 #ifdef UDPDEBUG
384         printf("ip_receive_cfg: %i %i ",buf[0],buf[1]);
385         dumprid(buf+2);
386         printf(" v%i tryid%u\n",ntohs(*(unsigned short*)(buf+8)),(unsigned int)ntohl(*(u_int32_t*)(buf+10)));
387 #endif
388                         {
389                                 ip_handshake_info *hsi=new ip_handshake_info(buf);
390                                 hsi->addr.add(fromaddr);
391                                 ip_handshake_info *lhsi=NULL;
392                                 p=peer_list.find_byid(hsi->id);
393 /*#ifdef UDPDEBUG
394                                 printf("hsi %i %i id=",hsi->type,hsi->state);
395                                 hsi->id.dump();
396                                 printf(" ver=%i tryid=%u\n",hsi->iver,hsi->tryid);
397                                 mprintf((0,"peer_list.find_byid=%p\n",p));
398 #endif*/
399                                 if (!p){
400                                         p=peer_list.find_unk_by_addr(fromaddr);
401 //                                      mprintf((0,"peer_list.find_by_addr=%p\n",p));
402                                         if (p){
403                                                 peer_list.make_full(p,hsi->id,hsi->iver,hsi->addr);
404                                         }
405                                 }
406                                 else
407                                         p->verify_addr(hsi->addr);
408                                 if (!p){
409                                         p=peer_list.add_hsi(hsi);
410 //                                      mprintf((0,"peer_list.add_hsi=%p\n",p));
411                                 }
412                                 lhsi=p->find_handshake();
413                                 lhsi->tryid=hsi->tryid;
414                                 if (p->addr.goodaddr==NULL){
415                                         p->addr.setgoodaddr(fromaddr);
416 //                                      lhsi->state&=STATE_INEEDINFO;
417                                 }
418                                 if (hsi->state&STATE_INEEDINFO)
419                                         lhsi->setstate(STATE_SENDINGINFO);
420                                         //lhsi->state&=STATE_SENDINGINFO;
421                                 else
422                                         lhsi->setstate(0);
423
424 //                              mprintf((0,"lhsi->state=%i\n",lhsi->state));
425 //                              mprintf((0,"hsi->state=%i\n",hsi->state));
426                                 if (lhsi->state)
427                                         p->send_handshake(lhsi);
428
429                                 delete hsi;
430                         }break;
431                 case IP_CFG_RELAY:
432                         {
433                                 ip_peer *rp;
434                                 ip_handshake_relay hsr(buf);
435 #ifdef UDPDEBUG
436                                 printf("ip_receive_cfg: %i %i ",buf[0],buf[1]);
437                                 dumprid(buf+2);
438                                 printf(" v%i r_id ",ntohs(*(unsigned short*)(buf+8)));
439                                 hsr.r_id.dump();
440                                 printf(" r_iv%i\n",hsr.r_iver);
441 #endif
442                                 p=peer_list.find_byid(hsr.id);
443                                 if (!p) {
444                                         mprintf((0,"relay from unknown peer\n"));
445                                         break;//hrm.
446                                 }
447                                 rp=peer_list.find_byid(hsr.r_id);
448                                 if (hsr.state&STATE_RELAYREPLY){
449                                         if (!rp) {
450                                                 mprintf((0,"relay reply for unknown peer\n"));
451                                                 break;//hrm.
452                                         }
453                                         ip_handshake_relay *rhsr=p->find_relay(rp->id);
454                                         if (!rhsr)
455                                                 break;
456                                         rhsr->setstate(0);
457 #ifdef UDPDEBUG
458                                         printf("**** ");
459                                         p->id.dump();
460                                         printf(" is ok with ");
461                                         rp->id.dump();
462                                         printf("\n");
463 #endif
464                                 }else{
465                                         if (!rp)
466                                                 rp=peer_list.add_full(hsr.r_id,hsr.r_iver,hsr.r_addr);
467                                         else
468                                                 rp->verify_addr(hsr.r_addr);
469
470                                         if (rp->addr.goodaddr==NULL){
471                                                 mprintf((0,"sending relayed handshake\n"));
472                                                 //handshake with relayed peer
473                                                 ip_handshake_info *lhsi=rp->find_handshake();
474 //                                              lhsi->setstate(STATE_INEEDINFO);
475                                                 if (lhsi->addstate(STATE_INEEDINFO));
476                                                         rp->send_handshake(lhsi);
477                                         }else{
478                                                 mprintf((0,"sending relayed reply\n"));
479                                                 //reply to relayer
480                                                 ip_handshake_relay rhsr(rp);
481                                                 rhsr.setstate(STATE_RELAYREPLY);
482                                                 p->send_handshake(&rhsr);
483                                         }
484                                 }
485                         }break;
486         }
487 }
488
489
490
491 int ipx_ip_GetMyAddress(void) {
492
493         int i;
494         u_int32_t myhandshakeid;
495
496         d_srand( timer_get_approx_seconds() );
497 //      printf("set my id to %u\n",myhandshakeid);
498
499         ip_my_addrs.clear();
500
501         if (!FindArg("-ip_nogetmyaddr"))
502                 arch_ip_get_my_addr(myport);//if we can't get an addr, then we can still probably play, just some NAT configs might not work.
503 //              if (arch_ip_get_my_addr(myport)) return -1;
504
505         if ((i=FindArg("-ip_myaddr"))){
506                 ip_addr ip;
507                 if (!ip.dns(Args[i+1],myport)){
508                         ip_my_addrs.add(ip);
509                 }
510         }
511
512         myhandshakeid=htonl(d_rand32());
513         //ipx_MyAddress[4]=qhbuf[2];
514         //ipx_MyAddress[5]=qhbuf[3];
515         if (ip_my_addrs.naddr){
516                 ip_addr ip=*ip_my_addrs.begin();
517                 ipx_MyAddress[4]=ip.addr[2];
518                 ipx_MyAddress[5]=ip.addr[3];
519         }else{
520                 ipx_MyAddress[4]=d_rand()%255;
521                 ipx_MyAddress[5]=d_rand()%255;
522         }
523         //memcpy(ipx_MyAddress+6,&handshake_buf.id,4);
524         memcpy(ipx_MyAddress+6,&myhandshakeid,4);
525
526 #ifdef UDPDEBUG
527         printf(MSGHDR "Using TCP/IP id ");
528         dumprid(ipx_MyAddress+4);
529         putchar('\n');
530 #endif
531         return 0;
532 }
533 /* Parse PORTSHIFT numeric parameter
534  */
535
536 unsigned short ip_portshift(unsigned short baseport, const char *cs)
537 {
538         long port;
539         unsigned short ports=htons(baseport);
540         if (cs){
541                 port=atol(cs);
542                 if (cs[0]=='-' || cs[0]=='+')//relative port
543 //              if (port<-PORTSHIFT_TOLERANCE || port>+PORTSHIFT_TOLERANCE)
544 //                      msg("Invalid portshift in \"%s\", tolerance is +/-%d",cs,PORTSHIFT_TOLERANCE);
545 //              else
546                         ports=htons(port+baseport);
547                 else
548                         ports=htons(port);//absolute port
549         }
550 //      memcpy(qhbuf+4,&ports,2);
551         return ports;
552 }
553