source: abuse/trunk/src/innet.cpp @ 545

Last change on this file since 545 was 545, checked in by Sam Hocevar, 10 years ago

core: fix a few security issues caused by unchecked string operations,
reported in ticket #25.

File size: 18.7 KB
Line 
1/*
2 *  Abuse - dark 2D side-scrolling platform game
3 *  Copyright (c) 1995 Crack dot Com
4 *  Copyright (c) 2005-2011 Sam Hocevar <sam@hocevar.net>
5 *
6 *  This software was released into the Public Domain. As with most public
7 *  domain software, no warranty is made or implied by Crack dot Com or
8 *  Jonathan Clark.
9 */
10
11#include "config.h"
12
13#include <stdio.h>
14
15#include "common.h"
16
17#include "demo.h"
18#include "specs.h"
19#include "level.h"
20#include "game.h"
21#include "dev.h"
22#include "timing.h"
23#include "fileman.h"
24#include "netface.h"
25
26#include "gserver.h"
27#include "gclient.h"
28#include "dprint.h"
29#include "netcfg.h"
30
31/*
32
33  This file is a combination of :
34    src/net/unix/unixnfc.c
35    src/net/unix/netdrv.c
36    src/net/unix/undrv.c
37
38    netdrv & undrv compile to a stand-alone program with talk with unixnfc
39via a FIFO in /tmp, using a RPC-like scheme.  This versions runs inside
40of a abuse and therefore is a bit simpler.
41*/
42
43base_memory_struct *base;   // points to shm_addr
44base_memory_struct local_base;
45net_address *net_server=NULL;
46net_protocol *prot=NULL;
47net_socket *comm_sock=NULL,*game_sock=NULL;
48extern char lsf[256];
49game_handler *game_face=NULL;
50int local_client_number=0;        // 0 is the server
51join_struct *join_array=NULL;      // points to an array of possible joining clients
52extern char *get_login();
53extern void set_login(char const *name);
54
55
56int net_init(int argc, char **argv)
57{
58    int i,x,db_level=0;
59    base=&local_base;
60
61    local_client_number=0;
62
63    if (!main_net_cfg)
64        main_net_cfg=new net_configuration;
65
66    for (i=1; i<argc; i++)
67    {
68        if (!strcmp(argv[i],"-nonet"))
69        {
70            printf( "Net: Disabled (-nonet)\n" );
71            return 0;
72        }
73        else if (!strcmp(argv[i],"-port"))
74        {
75            if( i == argc - 1 ||
76                !sscanf( argv[i + 1], "%d", &x ) ||
77                x < 1 || x > 0x7fff )
78            {
79                fprintf( stderr, "Net: Bad value following -port, use 1..32000\n" );
80                return 0;
81            }
82            else
83            {
84                main_net_cfg->port = x;
85            }
86        }
87        else if( !strcmp( argv[i], "-net" ) && i < argc-1 )
88        {
89            i++;
90            strncpy(main_net_cfg->server_name, argv[i],
91                    sizeof(main_net_cfg->server_name) - 1);
92            main_net_cfg->server_name[sizeof(main_net_cfg->server_name) - 1]
93                = '\0';
94            main_net_cfg->state = net_configuration::CLIENT;
95        }
96        else if (!strcmp(argv[i],"-ndb"))
97        {
98            if (i==argc-1 || !sscanf(argv[i+1],"%d",&x) || x<1 || x>3)
99            {
100                fprintf(stderr,"Net: Bad value following -ndb, use 1..3\n" );
101                return 0;
102            }
103            else
104            {
105                db_level = x;
106            }
107        }
108        else if( !strcmp( argv[i], "-server" ) )
109        {
110            main_net_cfg->state = net_configuration::SERVER;
111        }
112        else if( !strcmp( argv[i], "-min_players" ) )
113        {
114            i++;
115            int x = atoi( argv[i] );
116            if (x >= 1 && x <= 8)
117            {
118                main_net_cfg->min_players=x;
119            }
120            else
121            {
122                fprintf(stderr,"bad value for min_players use 1..8\n");
123            }
124        }
125    }
126
127    net_protocol *n = net_protocol::first, *usable = NULL;     // find a usable protocol from installed list
128    int total_usable = 0;
129    for( ; n; n = n->next )                                    // show a list of usables, just to be cute
130    {
131        fprintf( stderr, "Protocol %s : ",n->installed() ? "Installed" : "Not_installed" );
132        fprintf( stderr, "%s\n", n->name() );
133        if( n->installed() )
134        {
135            total_usable++;
136            usable=n;
137        }
138    }
139
140    if (!usable)
141    {
142        fprintf(stderr,"Net: No network protocols installed\n");
143        return 0;
144    }
145    prot=usable;
146    prot->set_debug_printing((net_protocol::debug_type)db_level);
147    if (main_net_cfg->state==net_configuration::SERVER)
148        set_login(main_net_cfg->name);
149
150    comm_sock=game_sock=NULL;
151    if (main_net_cfg->state==net_configuration::CLIENT)
152    {
153        dprintf("Attempting to locate server %s, please wait\n",main_net_cfg->server_name);
154        char const *sn=main_net_cfg->server_name;
155        net_server=prot->get_node_address(sn,DEFAULT_COMM_PORT,0);
156        if (!net_server)
157        {
158            dprintf(symbol_str("unable_locate"));
159            exit(0);
160        }
161        dprintf("Server located!  Please wait while data loads....\n");
162    }
163
164    fman=new file_manager(argc,argv,prot);                                       // manages remote file access
165    game_face=new game_handler;
166    join_array=(join_struct *)malloc(sizeof(join_struct)*MAX_JOINERS);
167    base->join_list=NULL;
168    base->mem_lock=0;
169    base->calc_crcs=0;
170    base->get_lsf=0;
171    base->wait_reload=0;
172    base->need_reload=0;
173    base->input_state=INPUT_COLLECTING;
174    base->current_tick=0;
175    base->packet.packet_reset();
176
177    return 1;
178}
179
180
181
182
183int net_start()  // is the game starting up off the net? (i.e. -net hostname)
184{   return (main_net_cfg && main_net_cfg->state==net_configuration::CLIENT);  }
185
186
187
188int kill_net()
189{
190  if (game_face) delete game_face;  game_face=NULL;
191  if (join_array) free(join_array);  join_array=NULL;
192  if (game_sock) { delete game_sock; game_sock=NULL; }
193  if (comm_sock) { delete comm_sock; comm_sock=NULL; }
194  delete fman;  fman=NULL;
195  if (net_server) { delete net_server; net_server=NULL; }
196  if (prot)
197  {
198
199    prot->cleanup();
200    prot=NULL;
201    return 1;
202  } else return 0;
203}
204
205void net_uninit()
206{
207  kill_net();
208}
209
210
211int NF_set_file_server(net_address *addr)
212{
213  if (prot)
214  {
215    fman->set_default_fs(addr);
216    net_socket *sock=prot->connect_to_server(addr,net_socket::SOCKET_SECURE);
217
218    if (!sock) { printf("set_file_server::connect failed\n"); return 0; }
219    uint8_t cmd=CLIENT_CRC_WAITER;
220    if ( (sock->write(&cmd,1)!=1 && printf("set_file_server::writefailed\n")) ||
221     (sock->read(&cmd,1)!=1  && printf("set_file_server::read failed\n")))        // wait for confirmation that crc's are written
222    { delete sock; return 0; }
223    delete sock;
224    return cmd;
225  } else return 0;
226}
227
228int NF_set_file_server(char const *name)
229{
230  if (prot)
231  {
232    net_address *addr=prot->get_node_address(name,DEFAULT_COMM_PORT,0);
233    if (addr)
234    {
235      int ret=NF_set_file_server(addr);
236      delete addr;
237      return ret;
238    } else return 0;
239  } else return 0;
240}
241
242
243int NF_open_file(char const *filename, char const *mode)
244{
245  if (prot)
246    return fman->rf_open_file(filename,mode);
247  else return -2;
248}
249
250
251long NF_close(int fd)
252{
253  if (prot)
254    return fman->rf_close(fd);
255  else return 0;
256}
257
258long NF_read(int fd, void *buf, long size)
259{
260  if (prot)
261    return fman->rf_read(fd,buf,size);
262  else return 0;
263}
264
265long NF_filelength(int fd)
266{
267  if (prot)
268    return fman->rf_file_size(fd);
269  else return 0;
270}
271
272long NF_seek(int fd, long offset)
273{
274  if (prot)
275    return fman->rf_seek(fd,offset);
276  else return 0;
277}
278
279long NF_tell(int fd)
280{
281  if (prot)
282    return fman->rf_tell(fd);
283  else return 0;
284}
285
286
287void service_net_request()
288{
289  if (prot)
290  {
291    if (prot->select(0))  // anything happening net-wise?
292    {
293      if (comm_sock && comm_sock->ready_to_read())  // new connection?
294      {
295                net_address *addr;
296
297                net_socket *new_sock=comm_sock->accept(addr);
298                if (new_sock)
299                {
300                  uint8_t client_type;
301                  if (new_sock->read(&client_type,1)!=1)
302                  {
303                    delete addr;
304                    delete new_sock;
305                  }
306                  else
307                  {
308                    switch (client_type)
309                    {
310                      case CLIENT_NFS :
311                      {
312                                delete addr;
313                                fman->add_nfs_client(new_sock);
314                      } break;
315                      case CLIENT_CRC_WAITER :
316                      {
317                                crc_manager.write_crc_file(NET_CRC_FILENAME);       // return 0 on failure
318                                client_type=1;                                  // confirmation byte
319                                new_sock->write(&client_type,1);
320                                delete new_sock;                                // done with this socket now
321                                delete addr;
322                      } break;
323                      case CLIENT_LSF_WAITER :          // wants to know which .lsp file to start with
324                      {
325                                uint8_t len=strlen(lsf);
326                                new_sock->write(&len,1);
327                                new_sock->write(lsf,len);
328                                delete new_sock;
329                                delete addr;
330                      } break;
331                      default :
332                      {
333                                if (game_face->add_client(client_type,new_sock,addr)==0)  // ask server or client to add new client
334                                {
335                                  delete addr;
336                                  delete new_sock;
337                                }
338                      } break;
339                    }
340                  }
341                }
342      }
343      if (!game_face->process_net())
344      {
345                delete game_face;
346                game_face=new game_handler;
347      }
348      fman->process_net();
349    }
350  }
351}
352
353
354int get_remote_lsf(net_address *addr, char *filename)  // filename should be 256 bytes
355{
356  if (prot)
357  {
358    net_socket *sock=prot->connect_to_server(addr,net_socket::SOCKET_SECURE);
359    if (!sock) return 0;
360
361    uint8_t ctype=CLIENT_LSF_WAITER;
362    uint8_t len;
363
364    if (sock->write(&ctype,1)!=1 ||
365                sock->read(&len,1)!=1 || len==0 ||
366                sock->read(filename,len)!=len)
367    {
368      delete sock;
369      return 0;
370    }
371
372    delete sock;
373    return 1;
374
375  } else return 0;
376}
377
378void server_check() { ; }
379
380int request_server_entry()
381{
382  if (prot && main_net_cfg)
383  {
384    if (!net_server) return 0;
385
386    if (game_sock) delete game_sock;
387    dprintf("Joining game in progress, hang on....\n");
388
389    game_sock=prot->create_listen_socket(main_net_cfg->port+2,net_socket::SOCKET_FAST);     // this is used for fast game packet transmission
390    if (!game_sock) { if (comm_sock) delete comm_sock; comm_sock=NULL; prot=NULL; return 0; }
391    game_sock->read_selectable();
392
393    net_socket *sock=prot->connect_to_server(net_server,net_socket::SOCKET_SECURE);
394    if (!sock)
395    {
396      fprintf(stderr,"unable to connect to server\n");
397      return 0;
398    }
399
400    uint8_t ctype=CLIENT_ABUSE;
401    uint16_t port=lstl(main_net_cfg->port+2),cnum;
402
403    uint8_t reg;
404    if (sock->write(&ctype,1)!=1 ||   // send server out game port
405                sock->read(&reg,1)!=1)        // is remote engine registered?
406    { delete sock; return 0; }
407
408    if (reg==2)   // too many players
409    {
410      fprintf(stderr, "%s", symbol_str("max_players"));
411      delete sock;
412      return 0;
413    }
414
415    // make sure the server is registered or sync problems will occur
416    if (!reg)
417    {
418      fprintf(stderr, "%s", symbol_str("server_not_reg"));
419      delete sock;
420      return 0;
421    }
422
423    char uname[256];
424    if (get_login())
425      strcpy(uname,get_login());
426    else strcpy(uname,"unknown");
427    uint8_t len=strlen(uname)+1;
428    short nkills;
429
430    if (sock->write(&len,1)!=1 ||
431                sock->write(uname,len)!=len ||
432                sock->write(&port,2)!=2  ||            // send server out game port
433                sock->read(&port,2)!=2   ||            // read server's game port
434                sock->read(&nkills,2)!=2 ||
435                sock->read(&cnum,2)!=2   || cnum==0    // read player number (cannot be 0 because 0 is server)
436                )
437    { delete sock; return 0; }
438
439    nkills=lstl(nkills);
440    port=lstl(port);
441    cnum=lstl(cnum);
442
443    main_net_cfg->kills=nkills;
444    net_address *addr=net_server->copy();
445    addr->set_port(port);
446
447    delete game_face;
448    game_face=new game_client(sock,addr);
449    delete addr;
450
451    local_client_number=cnum;
452    return cnum;
453  } else return 0;
454}
455
456int reload_start()
457{
458  if (prot)
459    return game_face->start_reload();
460  else return 0;
461}
462
463int reload_end()
464{
465  if (prot)
466    return game_face->end_reload();
467  else return 0;
468}
469
470
471void net_reload()
472{
473  if (prot)
474  {
475    if (net_server)
476    {
477      if (current_level)
478        delete current_level;
479      bFILE *fp;
480
481      if (!reload_start()) return ;
482
483      do {            // make sure server saves the file
484                fp=open_file(NET_STARTFILE,"rb");
485                if (fp->open_failure()) { delete fp; fp=NULL; }
486      } while (!fp);
487
488      spec_directory sd(fp);
489
490#if 0
491      spec_entry *e=sd.find("Copyright 1995 Crack dot Com, All Rights reserved");
492      if (!e)
493      {
494                the_game->show_help("This level is missing copyright information, cannot load\n");
495                current_level=new level(100,100,"untitled");
496                the_game->need_refresh();
497      }
498      else
499#endif
500        current_level=new level(&sd,fp,NET_STARTFILE);
501
502      delete fp;
503      base->current_tick=(current_level->tick_counter()&0xff);
504
505      reload_end();
506    } else if (current_level)
507    {
508
509      join_struct *join_list=base->join_list;
510
511
512      while (join_list)
513      {
514
515                view *f=player_list;
516                for (; f && f->next; f=f->next);      // find last player, add one for pn
517                int i,st=0;
518                for (i=0; i<total_objects; i++)
519                if (!strcmp(object_names[i],"START"))
520                st=i;
521
522                game_object *o=create(current_start_type,0,0);
523                game_object *start=current_level->get_random_start(320,NULL);
524                if (start) { o->x=start->x; o->y=start->y; }
525                else { o->x=100; o->y=100; }
526
527                f->next=new view(o,NULL,join_list->client_id);
528                strcpy(f->next->name,join_list->name);
529                o->set_controller(f->next);
530                f->next->set_tint(f->next->player_number);
531                if (start)
532                current_level->add_object_after(o,start);
533                else
534                current_level->add_object(o);
535
536                view *v=f->next;
537
538                v->cx1=5;
539                v->cy1=5;
540                v->cx2=319-5;
541                v->cy2=199-5;
542                join_list=join_list->next;
543      }
544      base->join_list=NULL;
545      current_level->save(NET_STARTFILE,1);
546      base->mem_lock=0;
547
548
549      Jwindow *j=wm->new_window(0,yres/2,-1,-1,new info_field(0, 0, 0, symbol_str("resync"),
550                          new button(0, wm->font()->height() + 5, ID_NET_DISCONNECT,
551                             symbol_str("slack"),NULL)),symbol_str("hold!"))
552;
553
554
555
556      wm->flush_screen();
557      if (!reload_start()) return ;
558
559      base->input_state=INPUT_RELOAD;    // if someone missed the game tick with the RELOAD data in it, make sure the get it
560
561      // wait for all client to reload the level with the new players
562      do
563      {
564                service_net_request();
565                if (wm->event_waiting())
566                {
567                  event ev;
568                  do
569                  {
570                    wm->get_event(ev);
571                    if (ev.type==EV_MESSAGE && ev.message.id==ID_NET_DISCONNECT)
572                    {
573                      game_face->end_reload(1);
574                      base->input_state=INPUT_PROCESSING;
575                    }
576
577                  } while (wm->event_waiting());
578
579                  wm->flush_screen();
580                }
581
582      } while (!reload_end());
583      wm->close_window(j);
584      unlink(NET_STARTFILE);
585
586      the_game->reset_keymap();
587
588      base->input_state=INPUT_COLLECTING;
589
590    }
591  }
592}
593
594
595int client_number() { return local_client_number; }
596
597
598void send_local_request()
599{
600
601  if (prot)
602  {
603    if (current_level)
604      base->current_tick=(current_level->tick_counter()&0xff);
605    game_face->add_engine_input();
606  } else base->input_state=INPUT_PROCESSING;
607
608}
609
610
611void kill_slackers()
612{
613  if (prot)
614  {
615    if (!game_face->kill_slackers())
616    {
617      delete game_face;
618      game_face=new game_handler();
619    }
620  }
621}
622
623int get_inputs_from_server(unsigned char *buf)
624{
625  if (prot && base->input_state!=INPUT_PROCESSING)      // if input is not here, wait on it
626  {
627    time_marker start;
628
629    int total_retry=0;
630    Jwindow *abort=NULL;
631
632    while (base->input_state!=INPUT_PROCESSING)
633    {
634      if (!prot)
635      {
636    base->input_state=INPUT_PROCESSING;
637    return 1;
638      }
639      service_net_request();
640
641      time_marker now;                   // if this is taking to long, the packet was probably lost, ask for it to be resent
642
643      if (now.diff_time(&start)>0.05)
644      {
645    if (prot->debug_level(net_protocol::DB_IMPORTANT_EVENT))
646      fprintf(stderr,"(missed packet)");
647
648
649    game_face->input_missing();
650    start.get_time();
651
652    total_retry++;
653    if (total_retry==12000)    // 2 minutes and nothing
654    {
655      abort=wm->new_window(0,yres/2,-1,wm->font()->height()*4,
656                   new info_field(0, 0, 0, symbol_str("waiting"),
657                          new button(0, wm->font()->height() + 5, ID_NET_DISCONNECT,
658                             symbol_str("slack"),NULL)),symbol_str("Error"));
659      wm->flush_screen();
660    }
661      }
662      if (abort)
663      {
664    if (wm->event_waiting())
665    {
666      event ev;
667      do
668      {
669        wm->get_event(ev);
670        if (ev.type==EV_MESSAGE && ev.message.id==ID_NET_DISCONNECT)
671        {
672          kill_slackers();
673          base->input_state=INPUT_PROCESSING;
674        }
675      } while (wm->event_waiting());
676
677      wm->flush_screen();
678    }
679      }
680    }
681
682    if (abort)
683    {
684      wm->close_window(abort);
685      the_game->reset_keymap();
686
687    }
688  }
689
690
691  memcpy(base->last_packet.data,base->packet.data,base->packet.packet_size()+base->packet.packet_prefix_size());
692
693  int size=base->packet.packet_size();
694  memcpy(buf,base->packet.packet_data(),size);
695
696  base->packet.packet_reset();
697  base->mem_lock=0;
698
699  return size;
700}
701
702int become_server(char *name)
703{
704  if (prot && main_net_cfg)
705  {
706    delete game_face;
707
708    if (comm_sock) delete comm_sock;
709    comm_sock=prot->create_listen_socket(main_net_cfg->port,net_socket::SOCKET_SECURE);   // this is used for incomming connections
710    if (!comm_sock) { prot=NULL; return 0; }
711    comm_sock->read_selectable();
712        prot->start_notify(0x9090, name, strlen(name));  // should we define a new socket for notifiers?
713
714    if (game_sock) delete game_sock;
715    game_sock=prot->create_listen_socket(main_net_cfg->port+1,net_socket::SOCKET_FAST);     // this is used for fast game packet transmission
716    if (!game_sock) { if (comm_sock) delete comm_sock; comm_sock=NULL; prot=NULL; return 0; }
717    game_sock->read_selectable();
718
719    game_face=new game_server;
720    local_client_number=0;
721    return 1;
722  }
723  return 0;
724}
725
726void read_new_views() { ; }
727
728
729void wait_min_players()
730{
731  if (game_face) game_face->game_start_wait();
732}
Note: See TracBrowser for help on using the repository browser.