source: golgotha/src/i4/network/tcpip.cc @ 608

Last change on this file since 608 was 80, checked in by Sam Hocevar, 15 years ago
  • Adding the Golgotha source code. Not sure what's going to be interesting in there, but since it's all public domain, there's certainly stuff to pick up.
File size: 15.5 KB
Line 
1/********************************************************************** <BR>
2  This file is part of Crack dot Com's free source code release of
3  Golgotha. <a href="http://www.crack.com/golgotha_release"> <BR> for
4  information about compiling & licensing issues visit this URL</a>
5  <PRE> If that doesn't help, contact Jonathan Clark at
6  golgotha_source@usa.net (Subject should have "GOLG" in it)
7***********************************************************************/
8
9
10#define PTHREAD_KERNEL  /* Needed for OK and NOTOK defines */
11
12
13
14#ifndef _WINDOWS
15#define SIZE_TYPE unsigned int
16#include <unistd.h>
17
18#include <pthread.h>
19#include <string.h>
20#include <stdio.h>
21#include <netdb.h>
22#include <errno.h>
23#include <netinet/in.h>
24#include <sys/time.h>
25#include <sys/types.h>
26#include <unistd.h>
27
28#define closesocket ::close
29#define OPTION_TYPE int
30#else
31
32#include <io.h>
33#include <windows.h>
34#define OPTION_TYPE BOOL
35#define SIZE_TYPE int
36
37#endif
38
39#include "error/error.hh"
40#include "network/net_prot.hh"
41#include "network/net_find.hh"
42#include "network/net_addr.hh"
43#include "network/net_sock.hh"
44
45#include "time/time.hh"
46#include "memory/array.hh"
47
48class i4_tcpip_protocol;
49extern i4_tcpip_protocol i4_tcpip_protocol_instance;
50
51static int i4_get_my_addr(sockaddr_in &host)
52{
53  char buf[256];
54  if (gethostname(buf, sizeof(buf))==0)
55  {
56    hostent *hp=gethostbyname(buf);
57    if (hp)
58    {
59      memset( (char*) &host,0, sizeof(host));
60      memcpy(&host.sin_addr,hp->h_addr,hp->h_length);
61      return 1;
62    }
63  }
64  return 0;
65}
66
67
68class i4_tcpip_address : public i4_net_address
69{
70public:
71  sockaddr_in addr;
72
73  virtual i4_net_address *copy()
74  {
75    i4_tcpip_address *a=new i4_tcpip_address;
76    a->addr=addr;
77    a->protocol=protocol;
78    return a;
79  }
80
81  virtual void set_port(int port)
82  {
83    addr.sin_port = htons(port);
84  }
85
86  virtual i4_bool equals(i4_net_address *other)
87  {
88    i4_tcpip_address *to=(i4_tcpip_address *)other;
89    if (protocol==other->protocol && addr.sin_addr.s_addr==to->addr.sin_addr.s_addr)
90      return i4_T;
91    else return i4_F;
92  }
93};
94
95
96
97class i4_tcpip_sock : public i4_net_socket
98{
99  int fd;
100  i4_socket_type stype;
101  i4_net_protocol *protocol;
102  i4_tcpip_protocol *prot() { return (i4_tcpip_protocol *)protocol; }
103public:
104  int er;
105
106  void set_select_status(i4_bool read, i4_bool write, i4_bool error);
107 
108  int listen(int port)
109  {
110    OPTION_TYPE opt=1;
111
112    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt))!=0)
113    {
114      i4_warning("setsockopt reuse failed");
115      return 0;
116    }
117   
118    sockaddr_in host;
119    memset( (char*) &host,0, sizeof(host));
120    host.sin_family = AF_INET;
121    host.sin_port = htons(port);
122    host.sin_addr.s_addr = htonl (INADDR_ANY);
123
124    if (bind(fd, (sockaddr *) &host,  sizeof(host))==-1)
125    {
126      i4_warning("bind failed");
127      return 0;
128    }
129
130    if (stype==I4_CONTINOUS_STREAM && ::listen(fd, 5)==-1)
131    {
132      i4_warning("listen failed");
133      return 0;
134    }
135     
136
137    return 1;
138  }
139
140  i4_bool connect(i4_net_address *addr)
141  {
142    i4_tcpip_address *a = (i4_tcpip_address *)addr;
143
144    if ((i4_tcpip_protocol *)a->protocol==&i4_tcpip_protocol_instance)
145    {
146      if (::connect(fd, (sockaddr *) &(a->addr), sizeof(a->addr))==-1)
147        return i4_F;
148      return i4_T;
149    }
150    else
151      i4_warning("address from another protocol");
152   
153    return i4_F;
154  }
155
156  i4_tcpip_sock(i4_socket_type stype,
157                i4_net_protocol *protocol)
158    : protocol(protocol), stype(stype)
159  {
160    fd=socket(AF_INET, stype==I4_CONTINOUS_STREAM ? SOCK_STREAM : SOCK_DGRAM, 0);
161
162    if (fd<0)
163    {
164      i4_warning("out of sockets");
165      er=1;
166    }
167    else er=0;
168  }
169
170  i4_tcpip_sock(int fd, i4_socket_type stype, i4_net_protocol *protocol)
171    : fd(fd), stype(stype), protocol(protocol)
172  { ; }
173
174  i4_bool accept(i4_net_socket *&sock, i4_net_address *&addr)
175  {
176    sockaddr_in a;
177    SIZE_TYPE size=sizeof(a);
178    int new_fd=::accept(fd, (sockaddr *)&a, &size);
179    if (new_fd==-1)
180      return i4_F;
181
182    sock=new i4_tcpip_sock(new_fd, stype, protocol);
183    addr=new i4_tcpip_address;
184    ((i4_tcpip_address *)addr)->addr=a;
185    addr->protocol=protocol;
186    return i4_T;
187  }
188
189  ~i4_tcpip_sock()
190  {
191    if (fd!=-1)
192      closesocket(fd);   
193  }
194 
195  virtual w32 read_from(void *buffer, w32 size, i4_net_address *&addr)
196  {
197    sockaddr_in a;
198    SIZE_TYPE as=sizeof(a);
199    int s=recvfrom(fd, (char *)buffer, size, 0, (sockaddr *)&a, &as);
200    addr=new i4_tcpip_address;
201    ((i4_tcpip_address *)addr)->addr=a;
202    addr->protocol=protocol;
203    return s;
204  }
205
206  virtual w32 read (void *buffer, w32 size)
207  {
208    return recv(fd, (char *)buffer, size, 0);
209  }
210
211  virtual w32 write(const void *buffer, w32 size)
212  {
213    return send(fd, (char *)buffer, size, 0);
214  }
215
216  virtual i4_bool ready_to_read()
217  {
218    fd_set s;
219    struct timeval tv={0,0};
220    FD_ZERO(&s);
221    FD_SET(fd, &s);
222   
223    select(fd+1, &s, NULL, NULL, &tv);   
224    return FD_ISSET(fd, &s);
225  }
226
227  virtual i4_bool ready_to_write()
228  {
229    fd_set s;
230    struct timeval tv={0,0};
231    FD_ZERO(&s);
232    FD_SET(fd, &s);
233   
234
235    select(fd+1, NULL, &s, NULL, &tv);   
236    return FD_ISSET(fd, &s);
237  }
238
239  virtual i4_bool error_occurred()
240  {
241    fd_set s;
242    struct timeval tv={0,0};
243    FD_ZERO(&s);
244    FD_SET(fd, &s);
245   
246    select(fd+1, NULL, NULL, &s, &tv);   
247    return FD_ISSET(fd, &s);
248  }
249};
250
251
252
253class i4_reply_str : public i4_str
254{
255public:
256  i4_reply_str(char *buf, int _len)
257    : i4_str(_len)
258  {
259    len=_len;
260    memcpy(ptr, buf, len);
261  }
262  void *data() { return ptr; }
263};
264
265class i4_tcpip_finder : public i4_finder_socket
266{
267  int poll_fd, listen_fd, listen_port;
268  int t;
269
270  sockaddr_in my_addr;
271
272  i4_array<server> replies;
273
274  i4_time_class last_ping;
275
276  i4_net_protocol *protocol;
277public:
278  int er;
279
280  int total_servers()
281  {
282    return replies.size();
283  }
284
285  virtual void get_server(int num, server &s)
286  {
287    s=replies[num];
288  }
289
290  ~i4_tcpip_finder()
291  {
292    if (poll_fd!=-1) closesocket(poll_fd);
293    if (listen_fd!=-1) closesocket(listen_fd);
294
295    for (int i=0; i<replies.size(); ++i)
296    {
297      delete replies[i].notification_string;
298      delete replies[i].addr;
299    }     
300  }
301
302  int setup(int poll_port, int listen_port)
303  {
304    poll_fd=-1; listen_fd=-1;
305
306    if (!i4_get_my_addr(my_addr))
307    { i4_warning("couldn't get my ip"); return 0; }
308
309    poll_fd=socket(AF_INET, SOCK_DGRAM, 0);
310    if (poll_fd<0)
311    { i4_warning("out of sockets"); return 0; }
312
313    sockaddr_in host;
314    memset( (char*) &host,0, sizeof(host));
315    host.sin_family = AF_INET;
316    host.sin_port = htons(poll_port);
317    host.sin_addr.s_addr = htonl (INADDR_BROADCAST);
318 
319    OPTION_TYPE opt=1;
320    if (setsockopt(poll_fd, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt))!=0)
321    { i4_warning("setsockopt broadcast failed"); return 0; }
322
323    opt=1;
324    if (setsockopt(poll_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt))!=0)
325    { i4_warning("setsockopt reuse failed"); return 0; }
326
327    if (connect(poll_fd, (sockaddr *) &host, sizeof(host))==-1)
328    { i4_warning("connect failed"); return 0; }
329
330
331    listen_fd=socket(AF_INET, SOCK_DGRAM, 0);
332    if (listen_fd<0)
333    { i4_warning("out of sockets"); return 0; }
334
335    host.sin_port = htons(listen_port);
336    host.sin_addr.s_addr = htonl (INADDR_ANY);
337
338    if (bind(listen_fd, (sockaddr *) &host,  sizeof(host))==-1)
339    { i4_warning("bind failed"); return 0; }
340
341    last_ping.get();
342    return 1;
343  }
344
345  i4_tcpip_finder(int poll_port, int listen_port, i4_net_protocol *protocol)
346    : replies(32, 64),
347      listen_port(listen_port),
348      protocol(protocol)
349  {
350    er=!setup(poll_port, listen_port);
351  }
352
353  i4_bool poll()
354  {
355    fd_set read_set;
356    FD_ZERO(&read_set);
357    FD_SET(listen_fd, &read_set);
358    struct timeval tv={0,0};
359
360    // see if there are any responses from servers
361    select(FD_SETSIZE,&read_set,0, 0, &tv);
362    if (FD_ISSET(listen_fd, &read_set))
363    {
364      w8 buf[512];
365      i4_tcpip_address r;
366
367      SIZE_TYPE from_len=sizeof(sockaddr_in);
368      if (recvfrom(listen_fd, (char *)buf, sizeof(buf), 0, (sockaddr *)&r.addr, &from_len)>2)
369      {
370        w16 s_len=(buf[0]<<8) | buf[1];
371        if (s_len<510)  // if this more than 510 then bad data in packet
372        {
373          int found=0;
374          if (r.addr.sin_addr.s_addr==my_addr.sin_addr.s_addr)
375            found=1;
376
377          // see if we already found heard from this guy
378          for (int i=0; i<replies.size(); i++)
379            if (r.addr.sin_addr.s_addr==
380                ((i4_tcpip_address *)replies[i].addr)->addr.sin_addr.s_addr)
381              found=1;
382
383          if (!found)
384          {
385            server s;
386            s.notification_string=new i4_reply_str((char *)buf+2, s_len);
387            s.addr=new i4_tcpip_address(r);
388            s.addr->protocol=protocol;
389
390            replies.add(s);
391            return i4_T;
392          }       
393        }
394      }
395    }
396 
397
398    i4_time_class now;
399
400    if (now.milli_diff(last_ping) > 1000)   // ping once a second
401    {
402      w8 reply_port[2];
403      reply_port[0]=(listen_port>>8);
404      reply_port[1]=(listen_port&0xff);
405
406      if (send(poll_fd,  (char *)reply_port, 2, 0)!=2)
407        i4_warning("ping server error");
408      last_ping.get();
409    }
410
411    return i4_F;
412  }
413};
414
415class i4_tcpip_notifier : public i4_notifier_socket
416{
417  int fd;
418  w8 reply_packet[512];
419public:
420  int er;
421
422  ~i4_tcpip_notifier()
423  {
424    if (fd==-1) closesocket(fd);
425  }
426
427  int setup(int port, const i4_const_str &notification_string)
428  {
429    fd=-1;
430
431    fd=socket(AF_INET, SOCK_DGRAM, 0);
432    if (fd<0)
433    { i4_warning("out of sockets"); return 0; }
434
435    OPTION_TYPE opt=1;
436    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt))!=0)
437    { i4_warning("setsockopt reuse failed"); return 0; }
438
439    sockaddr_in host;
440    memset( (char*) &host,0, sizeof(host));
441    host.sin_family = AF_INET;
442    host.sin_port = htons(port);
443    host.sin_addr.s_addr = htonl (INADDR_ANY);
444
445    if (bind(fd, (sockaddr *) &host,  sizeof(host))==-1)
446    { i4_warning("notify bind failed"); return 0; }
447
448    int s_len=notification_string.length();
449    reply_packet[0]=s_len>>8;
450    reply_packet[1]=s_len&0xff;
451
452    memcpy(reply_packet+2, ((i4_reply_str *)&notification_string)->data(), s_len);
453     
454    return 1;
455  }
456
457  i4_tcpip_notifier(int port,  const i4_const_str &notification_string) 
458  {
459    er=!setup(port, notification_string);   
460  }
461
462  virtual int poll()
463  {
464    fd_set read_set;
465    FD_ZERO(&read_set);
466    FD_SET(fd, &read_set);
467    struct timeval tv={0,0};
468
469    // see if there are any clients pinging us
470    select(FD_SETSIZE,&read_set,0, 0, &tv);
471    if (FD_ISSET(fd, &read_set))
472    {   
473      w8 buf[512];
474      sockaddr_in host;
475      SIZE_TYPE from_len=sizeof(sockaddr_in);
476      if (recvfrom(fd, (char *)buf, sizeof(buf), 0, (sockaddr *)&host, &from_len)==2)
477      {
478        int reply_port=(buf[0]<<8)|buf[1];
479        host.sin_port=htons(reply_port);
480        sendto(fd, (char *)reply_packet, sizeof(reply_packet), 0, (sockaddr *)&host, sizeof(host));
481        return 1;
482      }
483    }
484    return 0;
485
486  }
487};
488
489#ifdef __linux
490#include <stdio.h>
491#include <string.h>
492
493// linux gethostname doesn't seem to be thread-safe, so I'm making a system call
494int linux_name_to_address(char *hostname, unsigned int &ip)
495{
496  char buf[200];
497  sprintf(buf, "nslookup -ret=2 -timeout=2 %s", hostname);
498
499  fprintf(stderr,"\n*** %s **\n",buf);
500 
501  FILE *fp=popen(buf, "rb");
502    fprintf(stderr,"\n*** popen done fp=%p**\n",fp);
503  if (!fp) return 0;
504  while (!feof(fp))
505  {
506    fgets(buf,200,fp);
507    fprintf(stderr,"\n*** fgets %s**\n",buf);
508    if (strstr(buf, hostname))
509    {
510      fgets(buf,200,fp);
511      buf[strlen(buf)-1]=0;
512
513      char *c;
514      for (c=buf; *c!=':'; c++); c++;
515      for (; *c==' '; c++);
516
517      int i1,i2,i3,i4;
518      sscanf(c,"%d.%d.%d.%d", &i1,&i2,&i3,&i4);
519
520      char ret[4];
521      ret[0]=i1;  ret[1]=i2;  ret[2]=i3;  ret[3]=i4;
522
523      fclose(fp);
524      ip= *((unsigned long *)ret);
525
526      fprintf(stderr,"\n*** found ip **\n");
527      return 1;
528    }
529  }
530
531  fclose(fp);
532  return 0;
533}
534
535#endif
536
537
538class i4_tcpip_protocol : public i4_net_protocol
539{
540  int err;
541public:
542  fd_set rfdset, wfdset, efdset;
543  int max_fd;
544
545  int select(int milli_sec_timeout)
546  {
547    if (!milli_sec_timeout)
548      return ::select(max_fd, &rfdset, &wfdset, &efdset, 0);
549    else
550    {
551      int secs=milli_sec_timeout/1000;
552      int usecs=(milli_sec_timeout-(secs*1000)) *1000;
553     
554      struct timeval tv={secs, usecs};   
555      return ::select(max_fd, &rfdset, &wfdset, &efdset, &tv);
556    }
557  }
558 
559  i4_tcpip_protocol()
560  {
561    max_fd=0;
562    FD_ZERO(&rfdset);
563    FD_ZERO(&wfdset);
564    FD_ZERO(&efdset);
565   
566#ifdef _WINDOWS
567    WSADATA wsaData;
568    WORD ver=MAKEWORD(1,1);
569    err=WSAStartup(ver, &wsaData);
570#else
571    err=0;
572#endif
573   
574  }
575
576  ~i4_tcpip_protocol()
577  {
578#ifdef _WINDOWS
579    if (!err)
580      WSACleanup();
581#endif
582  }
583
584  i4_notifier_socket *create_notifier_socket(int port, const i4_const_str &notification_string)
585  {
586    if (err) return 0;
587
588    i4_tcpip_notifier *n=new i4_tcpip_notifier(port, notification_string);
589    if (n->er)
590    {
591      delete n;
592      return 0;
593    }
594    else
595      return n;
596  }
597
598  i4_finder_socket *create_finder_socket(int poll_port, int listen_port)
599  {
600    if (err) return 0;
601
602    i4_tcpip_finder *f=new i4_tcpip_finder(poll_port, listen_port, this);
603    if (f->er)
604    {
605      delete f;
606      return 0;
607    }
608    else
609      return f;
610  }
611
612  virtual i4_net_address *name_to_address(const i4_const_str &name)
613  {
614    if (err) return 0;
615
616    sockaddr_in host;
617    memset( (char*) &host,0, sizeof(host));
618         
619    char buf[256];
620    i4_os_string(name, buf, 256);
621       
622    //    if (linux_name_to_address(buf, host.sin_addr.s_addr))
623    // {
624
625    hostent *hp=gethostbyname(buf);
626    if (hp)
627    {     
628      memcpy(&host.sin_addr,hp->h_addr,hp->h_length);
629
630      host.sin_family = AF_INET;
631
632      i4_tcpip_address *a=new i4_tcpip_address;
633      a->addr=host;
634      a->protocol=this;
635      return a;
636    }
637    return 0;
638
639  }
640
641  i4_net_socket *connect(i4_net_address *addr, i4_socket_type stype)
642  {
643    if (err) return 0;
644    if (addr->protocol==this)
645    {
646      i4_tcpip_sock *s=new i4_tcpip_sock(stype, this);
647      if (s->er || !s->connect((i4_tcpip_address *)addr))
648      {
649        delete s;
650        return 0;
651      } else return s;
652    }
653    else i4_warning("address from another protocol");
654    return 0;
655  }
656
657  i4_net_socket *listen(int port, i4_socket_type stype)
658  {
659    if (err) return 0;
660
661    i4_tcpip_sock *s=new i4_tcpip_sock(stype, this);
662    if (s->er || !s->listen(port))
663    {
664      delete s;
665      return 0;
666    } else return s;
667  }
668
669  char *name() { return "TCP/IP"; }
670  i4_protocol_type type() { return I4_TCPIP; }
671};
672
673i4_tcpip_protocol i4_tcpip_protocol_instance;
674
675
676void i4_tcpip_sock::set_select_status(i4_bool read, i4_bool write, i4_bool error)
677{
678  i4_tcpip_protocol *p=prot();
679  if (read)
680    FD_SET(fd, &p->rfdset);
681  else
682    FD_CLR(fd, &p->rfdset);
683
684  if (write)
685    FD_SET(fd, &p->wfdset);
686  else
687    FD_CLR(fd, &p->wfdset);
688
689  if (error)
690    FD_SET(fd, &p->efdset);
691  else
692    FD_CLR(fd, &p->efdset);
693
694  if (fd>=p->max_fd)
695    p->max_fd=fd+1;
696}
697
Note: See TracBrowser for help on using the repository browser.