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