1/*
2
3	   Copyright (C) 1993-2012 Hewlett-Packard Company
4                         ALL RIGHTS RESERVED.
5
6  The enclosed software and documentation includes copyrighted works
7  of Hewlett-Packard Co. For as long as you comply with the following
8  limitations, you are hereby authorized to (i) use, reproduce, and
9  modify the software and documentation, and to (ii) distribute the
10  software and documentation, including modifications, for
11  non-commercial purposes only.
12
13  1.  The enclosed software and documentation is made available at no
14      charge in order to advance the general development of
15      high-performance networking products.
16
17  2.  You may not delete any copyright notices contained in the
18      software or documentation. All hard copies, and copies in
19      source code or object code form, of the software or
20      documentation (including modifications) must contain at least
21      one of the copyright notices.
22
23  3.  The enclosed software and documentation has not been subjected
24      to testing and quality control and is not a Hewlett-Packard Co.
25      product. At a future time, Hewlett-Packard Co. may or may not
26      offer a version of the software and documentation as a product.
27
28  4.  THE SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS".
29      HEWLETT-PACKARD COMPANY DOES NOT WARRANT THAT THE USE,
30      REPRODUCTION, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR
31      DOCUMENTATION WILL NOT INFRINGE A THIRD PARTY'S INTELLECTUAL
32      PROPERTY RIGHTS. HP DOES NOT WARRANT THAT THE SOFTWARE OR
33      DOCUMENTATION IS ERROR FREE. HP DISCLAIMS ALL WARRANTIES,
34      EXPRESS AND IMPLIED, WITH REGARD TO THE SOFTWARE AND THE
35      DOCUMENTATION. HP SPECIFICALLY DISCLAIMS ALL WARRANTIES OF
36      MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
37
38  5.  HEWLETT-PACKARD COMPANY WILL NOT IN ANY EVENT BE LIABLE FOR ANY
39      DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
40      (INCLUDING LOST PROFITS) RELATED TO ANY USE, REPRODUCTION,
41      MODIFICATION, OR DISTRIBUTION OF THE SOFTWARE OR DOCUMENTATION.
42
43*/
44
45#include "netperf_version.h"
46
47char	netserver_id[]="\
48@(#)netserver.c (c) Copyright 1993-2012 Hewlett-Packard Co. Version 2.6.0";
49
50
51#ifdef HAVE_CONFIG_H
52#include "config.h"
53#endif
54
55#if HAVE_STRING_H
56# if !STDC_HEADERS && HAVE_MEMORY_H
57#  include <memory.h>
58# endif
59# include <string.h>
60#endif
61
62#if HAVE_STRINGS_H
63# include <strings.h>
64#endif
65
66#if HAVE_LIMITS_H
67# include <limits.h>
68#endif
69
70#if HAVE_SYS_IPC_H
71#include <sys/ipc.h>
72#endif
73
74#if HAVE_SYS_IOCTL_H
75#include <sys/ioctl.h>
76#endif
77
78#if HAVE_SYS_SOCKET_H
79#include <sys/socket.h>
80#endif
81
82#if HAVE_SYS_STAT_H
83#include <sys/stat.h>
84#endif
85
86#if HAVE_NETINET_IN_H
87#include <netinet/in.h>
88#endif
89
90#if HAVE_NETDB_H
91#include <netdb.h>
92#endif
93
94#if HAVE_UNISTD_H
95#include <unistd.h>
96#endif
97
98#if HAVE_STDLIB_H
99#include <stdlib.h>
100#endif
101
102#if HAVE_ERRNO_H
103#include <errno.h>
104#endif
105
106#if HAVE_SIGNAL_H
107#include <signal.h>
108/* some OS's have SIGCLD defined as SIGCHLD */
109#ifndef SIGCLD
110#define SIGCLD SIGCHLD
111#endif /* SIGCLD */
112
113#endif
114
115#if !defined(HAVE_SETSID)
116#if HAVE_SYS_WAIT_H
117#include <sys/wait.h>
118#endif
119#endif
120
121#ifdef WIN32
122#include <time.h>
123#include <winsock2.h>
124
125#if HAVE_WS2TCPIP_H
126#include <ws2tcpip.h>
127#endif
128
129#include <windows.h>
130
131#include "missing\stdint.h"
132
133#define strdup _strdup
134#define sleep(x) Sleep((x)*1000)
135#define netperf_socklen_t socklen_t
136#endif /* WIN32 */
137
138/* unconditional system includes */
139
140#include <sys/types.h>
141#include <stdio.h>
142#include <fcntl.h>
143
144/* netperf includes */
145#include "netlib.h"
146#include "nettest_bsd.h"
147
148#ifdef WANT_UNIX
149#include "nettest_unix.h"
150#endif /* WANT_UNIX */
151
152#ifdef WANT_DLPI
153#include "nettest_dlpi.h"
154#endif /* WANT_DLPI */
155
156#ifdef WANT_SCTP
157#include "nettest_sctp.h"
158#endif
159
160#include "netsh.h"
161
162#ifndef DEBUG_LOG_FILE_DIR
163#if defined(WIN32)
164#define DEBUG_LOG_FILE_DIR ""
165#elif defined(ANDROID)
166#define DEBUG_LOG_FILE_DIR "/data/local/tmp/"
167#else
168#define DEBUG_LOG_FILE_DIR "/tmp/"
169#endif
170#endif /* DEBUG_LOG_FILE_DIR */
171
172#ifndef DEBUG_LOG_FILE
173#define DEBUG_LOG_FILE DEBUG_LOG_FILE_DIR"netserver.debug"
174#endif
175
176#if !defined(PATH_MAX)
177#define PATH_MAX MAX_PATH
178#endif
179char     FileName[PATH_MAX];
180
181char     listen_port[10];
182
183struct listen_elt {
184  SOCKET fd;
185  struct listen_elt *next;
186};
187
188struct listen_elt *listen_list = NULL;
189
190SOCKET   server_control;
191
192int      child;   /* are we the child of inetd or a parent netserver?
193		     */
194int      netperf_daemon;
195int      daemon_parent = 0;
196int      not_inetd;
197int      want_daemonize;
198int      spawn_on_accept;
199int      suppress_debug = 0;
200
201extern	char	*optarg;
202extern	int	optind, opterr;
203
204/* char  *passphrase = NULL; */
205
206static void
207init_netserver_globals() {
208
209#if defined(__VMS) || defined(VMWARE_UW)
210  spawn_on_accept = 0;
211  want_daemonize = 0;
212#else
213  spawn_on_accept = 1;
214#if defined(WIN32)
215  /* we only know how to spawn in WIN32, not daemonize */
216  want_daemonize = 0;
217#else
218  want_daemonize = 1;
219#endif /* WIN32 */
220#endif /* __VMS || VMWARE_UW */
221
222  child = 0;
223  not_inetd = 0;
224  netperf_daemon = 0;
225}
226
227void
228unlink_empty_debug_file() {
229
230#if !defined(WIN32)
231  struct stat buf;
232
233  if (stat(FileName,&buf)== 0) {
234
235    if (buf.st_size == 0)
236      unlink(FileName);
237  }
238#endif
239}
240
241/* it is important that set_server_sock() be called before this
242   routine as we depend on the control socket being dup()ed out of the
243   way when we go messing about with the streams. */
244void
245open_debug_file()
246{
247#if !defined WIN32
248#define NETPERF_NULL "/dev/null"
249#else
250#define NETPERF_NULL "nul"
251#endif
252
253  FILE *rd_null_fp;
254
255  if (where != NULL) fflush(where);
256
257  snprintf(FileName,
258	   sizeof(FileName),
259#if defined(WIN32)
260	   "%s\\%s_%d",
261	   getenv("TEMP"),
262#else
263	   "%s_%d",
264#endif
265	   DEBUG_LOG_FILE,
266	   getpid());
267  if ((where = fopen((suppress_debug) ? NETPERF_NULL : FileName,
268		     "w")) == NULL) {
269    perror("netserver: debug file");
270    exit(1);
271  }
272
273#if !defined(WIN32)
274
275  chmod(FileName,0644);
276
277  /* redirect stdin to "/dev/null" */
278  rd_null_fp = fopen(NETPERF_NULL,"r");
279  if (NULL == rd_null_fp) {
280    fprintf(where,
281	    "%s: opening of %s failed: %s (errno %d)\n",
282	    __FUNCTION__,
283	    NETPERF_NULL,
284	    strerror(errno),
285	    errno);
286    fflush(where);
287    exit(1);
288  }
289
290  if (close(STDIN_FILENO) == -1) {
291    fprintf(where,
292	    "%s: close of STDIN_FILENO failed: %s (errno %d)\n",
293	    __FUNCTION__,
294	    strerror(errno),
295	    errno);
296    fflush(where);
297    exit(1);
298  }
299
300  if (dup(fileno(rd_null_fp)) == -1) {
301    fprintf(where,
302	    "%s: dup of rd_null_fp to stdin failed: %s (errno %d)\n",
303	    __FUNCTION__,
304	    strerror(errno),
305	    errno);
306    fflush(where);
307    exit(1);
308  }
309
310  /* redirect stdout to "where" */
311  if (close(STDOUT_FILENO) == -1) {
312    fprintf(where,
313	    "%s: close of STDOUT_FILENO failed: %s (errno %d)\n",
314	    __FUNCTION__,
315	    strerror(errno),
316	    errno);
317    fflush(where);
318    exit(1);
319  }
320
321  if (dup(fileno(where)) == -1) {
322    fprintf(where,
323	    "%s: dup of where to stdout failed: %s (errno %d)\n",
324	    __FUNCTION__,
325	    strerror(errno),
326	    errno);
327    fflush(where);
328    exit(1);
329  }
330
331  /* redirect stderr to "where" */
332  if (close(STDERR_FILENO) == -1) {
333    fprintf(where,
334	    "%s: close of STDERR_FILENO failed: %s (errno %d)\n",
335	    __FUNCTION__,
336	    strerror(errno),
337	    errno);
338    fflush(where);
339    exit(1);
340  }
341
342  if (dup(fileno(where)) == -1) {
343    fprintf(where,
344	    "%s: dup of where to stderr failed: %s (errno %d)\n",
345	    __FUNCTION__,
346	    strerror(errno),
347	    errno);
348    fflush(where);
349    exit(1);
350  }
351
352#else
353
354  /* Hopefully, by closing stdout & stderr, the subsequent fopen calls
355     will get mapped to the correct std handles. */
356  fclose(stdout);
357
358  if ((where = fopen(FileName, "w")) == NULL) {
359    perror("netserver: fopen of debug file as new stdout failed!");
360    exit(1);
361  }
362
363  fclose(stderr);
364
365  if ((where = fopen(FileName, "w")) == NULL) {
366    fprintf(stdout, "fopen of debug file as new stderr failed!\n");
367    exit(1);
368  }
369
370#endif
371
372}
373
374/* so, either we are a child of inetd in which case server_sock should
375   be stdin, or we are a child of a netserver parent.  there will be
376   logic here for all of it, including Windows. it is important that
377   this be called before open_debug_file() */
378
379void
380set_server_sock() {
381
382  if (debug) {
383    fprintf(where,
384	    "%s: enter\n",
385	    __FUNCTION__);
386    fflush(where);
387  }
388
389#ifdef WIN32
390  server_sock = (SOCKET)GetStdHandle(STD_INPUT_HANDLE);
391#elif !defined(__VMS)
392  if (server_sock != INVALID_SOCKET) {
393    fprintf(where,"Yo, Iz ain't invalid!\n");
394    fflush(where);
395    exit(1);
396  }
397
398  /* we dup this to up the reference count so when we do redirection
399     of the io streams we don't accidentally toast the control
400     connection in the case of our being a child of inetd. */
401  server_sock = dup(0);
402
403#else
404  if ((server_sock =
405       socket(TCPIP$C_AUXS, SOCK_STREAM, 0)) == INVALID_SOCKET) {
406    fprintf(stderr,
407	    "%s: failed to grab aux server socket: %s (errno %s)\n",
408	    __FUNCTION__,
409	    strerror(errno),
410	    errno);
411    fflush(stderr);
412    exit(1);
413  }
414#endif
415
416}
417
418
419void
420create_listens(char hostname[], char port[], int af) {
421
422  struct addrinfo hints;
423  struct addrinfo *local_res;
424  struct addrinfo *local_res_temp;
425  int count, error;
426  int on = 1;
427  SOCKET temp_socket;
428  struct listen_elt *temp_elt;
429
430  if (debug) {
431    fprintf(stderr,
432	    "%s: called with host '%s' port '%s' family %s(%d)\n",
433	    __FUNCTION__,
434            hostname,
435	    port,
436	    inet_ftos(af),
437            af);
438    fflush(stderr);
439  }
440 memset(&hints,0,sizeof(hints));
441  hints.ai_family = af;
442  hints.ai_socktype = SOCK_STREAM;
443  hints.ai_protocol = IPPROTO_TCP;
444  hints.ai_flags = AI_PASSIVE;
445
446  count = 0;
447  do {
448    error = getaddrinfo((char *)hostname,
449                        (char *)port,
450                        &hints,
451                        &local_res);
452    count += 1;
453    if (error == EAI_AGAIN) {
454      if (debug) {
455        fprintf(stderr,
456		"%s: Sleeping on getaddrinfo EAI_AGAIN\n",
457		__FUNCTION__);
458        fflush(stderr);
459      }
460      sleep(1);
461    }
462  } while ((error == EAI_AGAIN) && (count <= 5));
463
464  if (error) {
465    if (debug) {
466
467      fprintf(stderr,
468	      "%s: could not resolve remote '%s' port '%s' af %d\n"
469	      "\tgetaddrinfo returned %s (%d)\n",
470	      __FUNCTION__,
471	      hostname,
472	      port,
473	      af,
474	      gai_strerror(error),
475	      error);
476
477    }
478    return;
479  }
480
481  if (debug) {
482    dump_addrinfo(stderr, local_res, hostname, port, af);
483  }
484
485  local_res_temp = local_res;
486
487  while (local_res_temp != NULL) {
488
489    temp_socket = socket(local_res_temp->ai_family,SOCK_STREAM,0);
490
491    if (temp_socket == INVALID_SOCKET) {
492      if (debug) {
493	fprintf(stderr,
494		"%s could not allocate a socket: %s (errno %d)\n",
495		__FUNCTION__,
496		strerror(errno),
497		errno);
498	fflush(stderr);
499      }
500      local_res_temp = local_res_temp->ai_next;
501      continue;
502    }
503
504    /* happiness and joy, keep going */
505    if (setsockopt(temp_socket,
506		   SOL_SOCKET,
507		   SO_REUSEADDR,
508		   (char *)&on ,
509		   sizeof(on)) == SOCKET_ERROR) {
510      if (debug) {
511	fprintf(stderr,
512		"%s: warning: could not set SO_REUSEADDR: %s (errno %d)\n",
513		__FUNCTION__,
514		strerror(errno),
515		errno);
516	fflush(stderr);
517      }
518    }
519    /* still happy and joyful */
520
521    if ((bind(temp_socket,
522	      local_res_temp->ai_addr,
523	      local_res_temp->ai_addrlen) != SOCKET_ERROR) &&
524	(listen(temp_socket,1024) != SOCKET_ERROR))  {
525
526      /* OK, now add to the list */
527      temp_elt = (struct listen_elt *)malloc(sizeof(struct listen_elt));
528      if (temp_elt) {
529	temp_elt->fd = temp_socket;
530	if (listen_list) {
531	  temp_elt->next = listen_list;
532	}
533	else {
534	  temp_elt->next = NULL;
535	}
536	listen_list = temp_elt;
537      }
538      else {
539	fprintf(stderr,
540		"%s: could not malloc a listen_elt\n",
541		__FUNCTION__);
542	fflush(stderr);
543	exit(1);
544      }
545    }
546    else {
547      /* we consider a bind() or listen() failure a transient and try
548	 the next address */
549      if (debug) {
550	fprintf(stderr,
551		"%s: warning: bind or listen call failure: %s (errno %d)\n",
552		__FUNCTION__,
553		strerror(errno),
554		errno);
555	fflush(stderr);
556      }
557      close(temp_socket);
558    }
559    local_res_temp = local_res_temp->ai_next;
560  }
561
562}
563
564void
565setup_listens(char name[], char port[], int af) {
566
567  int do_inet;
568  int no_name = 0;
569#ifdef AF_INET6
570  int do_inet6;
571#endif
572
573  if (debug) {
574    fprintf(where,
575	    "%s: enter\n",
576	    __FUNCTION__);
577    fflush(where);
578  }
579
580
581  if (strcmp(name,"") == 0) {
582    no_name = 1;
583    switch (af) {
584    case AF_UNSPEC:
585      do_inet = 1;
586#ifdef AF_INET6
587      do_inet6 = 1;
588#endif
589      break;
590    case AF_INET:
591      do_inet = 1;
592#ifdef AF_INET6
593      do_inet6 = 0;
594#endif
595      break;
596#ifdef AF_INET6
597    case AF_INET6:
598      do_inet = 0;
599      do_inet6 = 1;
600      break;
601#endif
602    default:
603      do_inet = 1;
604    }
605    /* if we have IPv6, try that one first because it may be a superset */
606#ifdef AF_INET6
607    if (do_inet6)
608      create_listens("::0",port,AF_INET6);
609#endif
610    if (do_inet)
611      create_listens("0.0.0.0",port,AF_INET);
612  }
613  else {
614    create_listens(name,port,af);
615  }
616
617  if (listen_list) {
618    fprintf(stdout,
619	    "Starting netserver with host '%s' port '%s' and family %s\n",
620	    (no_name) ? "IN(6)ADDR_ANY" : name,
621	    port,
622	    inet_ftos(af));
623    fflush(stdout);
624  }
625  else {
626    fprintf(stderr,
627	    "Unable to start netserver with  '%s' port '%s' and family %s\n",
628	    (no_name) ? "IN(6)ADDR_ANY" : name,
629	    port,
630	    inet_ftos(af));
631    fflush(stderr);
632    exit(1);
633  }
634}
635
636SOCKET
637set_fdset(struct listen_elt *list, fd_set *fdset) {
638
639  struct listen_elt *temp;
640  SOCKET max = INVALID_SOCKET;
641
642  FD_ZERO(fdset);
643
644  temp = list;
645
646  if (debug) {
647    fprintf(where,
648	    "%s: enter list %p fd_set %p\n",
649	    __FUNCTION__,
650	    list,
651	    fdset);
652    fflush(where);
653  }
654
655  while (temp) {
656    if (temp->fd > max)
657      max = temp->fd;
658
659    if (debug) {
660      fprintf(where,
661	      "setting %d in fdset\n",
662	      temp->fd);
663      fflush(where);
664    }
665
666    FD_SET(temp->fd,fdset);
667
668    temp = temp->next;
669  }
670
671  return max;
672
673}
674
675void
676close_listens(struct listen_elt *list) {
677  struct listen_elt *temp;
678
679  if (debug) {
680    fprintf(where,
681	    "%s: enter\n",
682	    __FUNCTION__);
683    fflush(where);
684  }
685
686  temp = list;
687
688  while (temp) {
689    close(temp->fd);
690    temp = temp->next;
691  }
692}
693
694static int
695recv_passphrase() {
696
697  /* may need to revisit the timeout. we only respond if there is an
698     error with receiving the passphrase */
699  if ((recv_request_timed_n(0,20) > 0) &&
700      (netperf_request.content.request_type == PASSPHRASE) &&
701      (!strcmp(passphrase,
702	       (char *)netperf_request.content.test_specific_data))) {
703    /* it was okey dokey */
704    return 0;
705  }
706#if defined(SEND_PASSPHRASE_RESPONSE)
707  netperf_response.content.response_type = PASSPHRASE;
708  netperf_response.content.serv_errno = 403;
709    snprintf((char *)netperf_response.content.test_specific_data,
710	     sizeof(netperf_response.content.test_specific_data),
711	     "Sorry, unable to match with required passphrase\n");
712  send_response_n(0);
713#endif
714  fprintf(where,
715	  "Unable to match required passphrase.  Closing control connection\n");
716  fflush(where);
717
718  close(server_sock);
719  return -1;
720}
721
722/* This routine implements the "main event loop" of the netperf server
723   code. Code above it will have set-up the control connection so it
724   can just merrily go about its business, which is to "schedule"
725   performance tests on the server.  */
726
727void
728process_requests()
729{
730
731  float	temp_rate;
732
733  if (debug) {
734    fprintf(where,
735	    "%s: enter\n",
736	    __FUNCTION__);
737    fflush(where);
738  }
739
740  /* if the netserver was started with a passphrase, look for it in
741     the first request to arrive.  if there is no passphrase in the
742     first request we will end-up dumping the control connection. raj
743     2012-01-23 */
744
745  if ((passphrase != NULL)  && (recv_passphrase()))
746      return;
747
748  while (1) {
749
750    if (recv_request() <= 0) {
751      close(server_sock);
752      return;
753    }
754
755    switch (netperf_request.content.request_type) {
756
757    case DEBUG_ON:
758      netperf_response.content.response_type = DEBUG_OK;
759      if (!suppress_debug) {
760	debug++;
761
762	if (debug == 1) {
763	  /* we just flipped-on debugging, dump the request because
764	     recv_request/recv_request_n will not have dumped it as its
765	     dump_request() call is conditional on debug being set. raj
766	     2011-07-08 */
767	  dump_request();
768	}
769      }
770
771      send_response();
772      break;
773
774    case DEBUG_OFF:
775      if (debug)
776	debug--;
777      netperf_response.content.response_type = DEBUG_OK;
778      send_response();
779      /* we used to take the trouble to close the debug file, but SAF
780	 asked a good question when he asked "Why?" and since I cannot
781	 think of a good reason, I have removed the code. raj
782	 2011-07-08 */
783      break;
784
785    case DO_SYSINFO:
786      {
787	netperf_response.content.response_type = SYSINFO_RESPONSE;
788
789	snprintf((char *)netperf_response.content.test_specific_data,
790		 sizeof(netperf_response.content.test_specific_data),
791		 "%c%s%c%s%c%s%c%s",
792		 ',',
793		 "Deprecated",
794		 ','
795,		 "Deprecated",
796		 ',',
797		 "Deprecated",
798		 ',',
799		 "Deprecated");
800
801	send_response_n(0);
802	break;
803      }
804
805    case CPU_CALIBRATE:
806      netperf_response.content.response_type = CPU_CALIBRATE;
807      temp_rate = calibrate_local_cpu(0.0);
808      bcopy((char *)&temp_rate,
809	    (char *)netperf_response.content.test_specific_data,
810	    sizeof(temp_rate));
811      bcopy((char *)&lib_num_loc_cpus,
812	    (char *)netperf_response.content.test_specific_data +
813	            sizeof(temp_rate),
814	    sizeof(lib_num_loc_cpus));
815      if (debug) {
816	fprintf(where,
817		"netserver: sending CPU information: rate is %g num cpu %d\n",
818		temp_rate,
819		lib_num_loc_cpus);
820	fflush(where);
821      }
822
823      /* we need the cpu_start, cpu_stop in the looper case to kill
824         the child proceses raj 7/95 */
825
826#ifdef USE_LOOPER
827      cpu_start(1);
828      cpu_stop(1,&temp_rate);
829#endif /* USE_LOOPER */
830
831      send_response();
832      break;
833
834    case DO_TCP_STREAM:
835      recv_tcp_stream();
836      break;
837
838    case DO_TCP_MAERTS:
839      recv_tcp_maerts();
840      break;
841
842    case DO_TCP_RR:
843      recv_tcp_rr();
844      break;
845
846    case DO_TCP_CRR:
847      recv_tcp_conn_rr();
848      break;
849
850    case DO_TCP_CC:
851      recv_tcp_cc();
852      break;
853
854#ifdef DO_1644
855    case DO_TCP_TRR:
856      recv_tcp_tran_rr();
857      break;
858#endif /* DO_1644 */
859
860#ifdef DO_NBRR
861    case DO_TCP_NBRR:
862      recv_tcp_nbrr();
863      break;
864#endif /* DO_NBRR */
865
866    case DO_UDP_STREAM:
867      recv_udp_stream();
868      break;
869
870    case DO_UDP_RR:
871      recv_udp_rr();
872      break;
873
874#ifdef WANT_DLPI
875
876    case DO_DLPI_CO_RR:
877      recv_dlpi_co_rr();
878      break;
879
880    case DO_DLPI_CL_RR:
881      recv_dlpi_cl_rr();
882      break;
883
884    case DO_DLPI_CO_STREAM:
885      recv_dlpi_co_stream();
886      break;
887
888    case DO_DLPI_CL_STREAM:
889      recv_dlpi_cl_stream();
890      break;
891
892#endif /* WANT_DLPI */
893
894#ifdef WANT_UNIX
895
896    case DO_STREAM_STREAM:
897      recv_stream_stream();
898      break;
899
900    case DO_STREAM_RR:
901      recv_stream_rr();
902      break;
903
904    case DO_DG_STREAM:
905      recv_dg_stream();
906      break;
907
908    case DO_DG_RR:
909      recv_dg_rr();
910      break;
911
912#endif /* WANT_UNIX */
913
914#ifdef WANT_XTI
915    case DO_XTI_TCP_STREAM:
916      recv_xti_tcp_stream();
917      break;
918
919    case DO_XTI_TCP_RR:
920      recv_xti_tcp_rr();
921      break;
922
923    case DO_XTI_UDP_STREAM:
924      recv_xti_udp_stream();
925      break;
926
927    case DO_XTI_UDP_RR:
928      recv_xti_udp_rr();
929      break;
930
931#endif /* WANT_XTI */
932
933#ifdef WANT_SCTP
934    case DO_SCTP_STREAM:
935      recv_sctp_stream();
936      break;
937
938    case DO_SCTP_STREAM_MANY:
939      recv_sctp_stream_1toMany();
940      break;
941
942    case DO_SCTP_RR:
943      recv_sctp_rr();
944      break;
945
946    case DO_SCTP_RR_MANY:
947      recv_sctp_rr_1toMany();
948      break;
949#endif
950
951#ifdef WANT_SDP
952    case DO_SDP_STREAM:
953      recv_sdp_stream();
954      break;
955
956    case DO_SDP_MAERTS:
957      recv_sdp_maerts();
958      break;
959
960    case DO_SDP_RR:
961      recv_sdp_rr();
962      break;
963#endif
964
965#ifdef WANT_OMNI
966    case DO_OMNI:
967      recv_omni();
968      break;
969#endif
970
971    case PASSPHRASE:
972      if (debug) {
973	fprintf(where,"Ignoring an unexpected passphrase control message\n");
974	fflush(where);
975      }
976      break;
977
978    default:
979      fprintf(where,"unknown test number %d\n",
980	      netperf_request.content.request_type);
981      fflush(where);
982      netperf_response.content.serv_errno=998;
983      send_response();
984      break;
985
986    }
987  }
988}
989
990/* the routine we call when we are going to spawn/fork/whatnot a child
991   process from the parent netserver daemon. raj 2011-07-08 */
992void
993spawn_child() {
994
995#if defined(HAVE_FORK)
996
997  if (debug) {
998    fprintf(where,
999	    "%s: enter\n",
1000	    __FUNCTION__);
1001    fflush(where);
1002  }
1003
1004
1005  /* flush the usual suspects */
1006  fflush(stdin);
1007  fflush(stdout);
1008  fflush(stderr);
1009  fflush(where);
1010
1011  signal(SIGCLD,SIG_IGN);
1012
1013  switch (fork()) {
1014  case -1:
1015    fprintf(where,
1016	    "%s: fork() error %s (errno %d)\n",
1017	    __FUNCTION__,
1018	    strerror(errno),
1019	    errno);
1020    fflush(where);
1021    exit(1);
1022
1023  case 0:
1024    /* we are the child, but not of inetd.  we don't know if we are
1025       the child of a daemonized parent or not, so we still need to
1026       worry about the standard file descriptors. raj 2011-07-11 */
1027
1028    close_listens(listen_list);
1029    open_debug_file();
1030
1031    child = 1;
1032    netperf_daemon = 0;
1033    process_requests();
1034    exit(0);
1035    break;
1036
1037  default:
1038    /* we are the parent, not a great deal to do here, but we may
1039       want to reap some children */
1040#if !defined(HAVE_SETSID)
1041    /* Only call "waitpid()" if "setsid()" is not used. */
1042    while(waitpid(-1, NULL, WNOHANG) > 0) {
1043      if (debug) {
1044	fprintf(where,
1045		"%s: reaped a child process\n",
1046		__FUNCTION__);
1047      }
1048    }
1049#endif
1050    break;
1051  }
1052
1053#elif defined(WIN32)
1054
1055  BOOL b;
1056  char *cmdline;
1057  int cmdline_length;
1058  int cmd_index;
1059  PROCESS_INFORMATION pi;
1060  STARTUPINFO si;
1061  int i;
1062
1063  if (debug) {
1064    fprintf(where,
1065	    "%s: enter\n",
1066	    __FUNCTION__);
1067    fflush(where);
1068  }
1069
1070
1071  /* create the cmdline array based on strlen(program) + 80 chars */
1072  cmdline_length = strlen(program) + 80;
1073  cmdline = malloc(cmdline_length + 1);  // +1 for trailing null
1074
1075  memset(&si, 0 , sizeof(STARTUPINFO));
1076  si.cb = sizeof(STARTUPINFO);
1077
1078  /* Pass the server_sock as stdin for the new process.  Hopefully
1079     this will continue to be created with the OBJ_INHERIT
1080     attribute. */
1081  si.hStdInput = (HANDLE)server_sock;
1082  si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1083  si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1084  si.dwFlags = STARTF_USESTDHANDLES;
1085
1086  /* Build cmdline for child process */
1087  strcpy(cmdline, program);
1088  cmd_index = strlen(cmdline);
1089  if (verbosity > 1) {
1090    cmd_index += snprintf(&cmdline[cmd_index],
1091			  cmdline_length - cmd_index,
1092			  " -v %d",
1093			  verbosity);
1094  }
1095  for (i=0; i < debug; i++) {
1096    cmd_index += snprintf(&cmdline[cmd_index],
1097			  cmdline_length - cmd_index,
1098			  " -d");
1099  }
1100  cmd_index += snprintf(&cmdline[cmd_index],
1101			cmdline_length - cmd_index,
1102			" -I %x",
1103			(int)(UINT_PTR)server_sock);
1104
1105  /* are these -i settings even necessary? the command line scanning
1106     does not seem to do anything with them */
1107  cmd_index += snprintf(&cmdline[cmd_index],
1108			cmdline_length - cmd_index,
1109			" -i %x",
1110			(int)(UINT_PTR)server_control);
1111  cmd_index += snprintf(&cmdline[cmd_index],
1112			cmdline_length - cmd_index,
1113			" -i %x",
1114			(int)(UINT_PTR)where);
1115
1116  b = CreateProcess(NULL,    /* Application Name */
1117		    cmdline,
1118		    NULL,    /* Process security attributes */
1119		    NULL,    /* Thread security attributes */
1120		    TRUE,    /* Inherit handles */
1121		    0,       /* Creation flags
1122				PROCESS_QUERY_INFORMATION,  */
1123		    NULL,    /* Enviornment */
1124		    NULL,    /* Current directory */
1125		    &si,     /* StartupInfo */
1126		    &pi);
1127  if (!b)
1128    {
1129      perror("CreateProcessfailure: ");
1130      free(cmdline); /* even though we exit :) */
1131      exit(1);
1132    }
1133
1134  /* We don't need the thread or process handles any more;
1135     let them go away on their own timeframe. */
1136
1137  CloseHandle(pi.hThread);
1138  CloseHandle(pi.hProcess);
1139
1140  /* the caller/parent will close server_sock */
1141
1142  free(cmdline);
1143
1144#else
1145
1146  fprintf(where,
1147	  "%s called on platform which cannot spawn children\n",
1148	  __FUNCTION__);
1149  fflush(where);
1150  exit(1);
1151
1152#endif /* HAVE_FORK */
1153}
1154
1155void
1156accept_connection(SOCKET listen_fd) {
1157
1158  struct sockaddr_storage peeraddr;
1159  netperf_socklen_t peeraddrlen;
1160#if defined(SO_KEEPALIVE)
1161  int on = 1;
1162#endif
1163
1164  if (debug) {
1165    fprintf(where,
1166	    "%s: enter\n",
1167	    __FUNCTION__);
1168    fflush(where);
1169  }
1170
1171  peeraddrlen = sizeof(peeraddr);
1172
1173  /* while server_control is only used by the WIN32 path, but why
1174     bother ifdef'ing it?  and besides, do we *really* need knowledge
1175     of server_control in the WIN32 case? do we have to tell the
1176     child about *all* the listen endpoints? raj 2011-07-08 */
1177  server_control = listen_fd;
1178
1179  if ((server_sock = accept(listen_fd,
1180			   (struct sockaddr *)&peeraddr,
1181			    &peeraddrlen)) == INVALID_SOCKET) {
1182    fprintf(where,
1183	    "%s: accept failure: %s (errno %d)\n",
1184	    __FUNCTION__,
1185	    strerror(errno),
1186	    errno);
1187    fflush(where);
1188    exit(1);
1189  }
1190
1191#if defined(SO_KEEPALIVE)
1192  /* we are not terribly concerned if this does not work, it is merely
1193     duct tape added to belts and suspenders. raj 2011-07-08 */
1194  setsockopt(server_sock,
1195	     SOL_SOCKET,
1196	     SO_KEEPALIVE,
1197	     (const char *)&on,
1198	     sizeof(on));
1199#endif
1200
1201  if (spawn_on_accept) {
1202    spawn_child();
1203    /* spawn_child() only returns when we are the parent */
1204    close(server_sock);
1205  }
1206  else {
1207    process_requests();
1208  }
1209}
1210
1211void
1212accept_connections() {
1213
1214  fd_set read_fds, write_fds, except_fds;
1215  SOCKET high_fd, candidate;
1216  int num_ready;
1217
1218  if (debug) {
1219    fprintf(where,
1220	    "%s: enter\n",
1221	    __FUNCTION__);
1222    fflush(where);
1223  }
1224
1225  while (1) {
1226
1227    FD_ZERO(&write_fds);
1228    FD_ZERO(&except_fds);
1229    high_fd = set_fdset(listen_list,&read_fds);
1230
1231#if !defined(WIN32)
1232    num_ready = select(high_fd + 1,
1233#else
1234    num_ready = select(1,
1235#endif
1236		       &read_fds,
1237		       &write_fds,
1238		       &except_fds,
1239		       NULL);
1240
1241    if (num_ready < 0) {
1242      fprintf(where,
1243	      "%s: select failure: %s (errno %d)\n",
1244	      __FUNCTION__,
1245	      strerror(errno),
1246	      errno);
1247      fflush(where);
1248      exit(1);
1249    }
1250
1251    /* try to keep things simple */
1252    candidate = 0;
1253    while ((num_ready) && (candidate <= high_fd)) {
1254      if (FD_ISSET(candidate,&read_fds)) {
1255	accept_connection(candidate);
1256	FD_CLR(candidate,&read_fds);
1257	num_ready--;
1258      }
1259      else {
1260	candidate++;
1261      }
1262    }
1263  }
1264}
1265
1266#ifndef WIN32
1267#define SERVER_ARGS "DdfhL:n:Np:v:VZ:46"
1268#else
1269#define SERVER_ARGS "DdfhL:n:Np:v:VZ:46I:i:"
1270#endif
1271void
1272scan_netserver_args(int argc, char *argv[]) {
1273
1274  int c;
1275  char arg1[BUFSIZ], arg2[BUFSIZ];
1276
1277  if (debug) {
1278    fprintf(where,
1279	    "%s: enter\n",
1280	    __FUNCTION__);
1281    fflush(where);
1282  }
1283
1284  while ((c = getopt(argc, argv, SERVER_ARGS)) != EOF){
1285    switch (c) {
1286    case '?':
1287    case 'h':
1288      print_netserver_usage();
1289      exit(1);
1290    case 'd':
1291      debug++;
1292      suppress_debug = 0;
1293      break;
1294    case 'D':
1295      /* perhaps one of these days we'll take an argument */
1296      want_daemonize = 0;
1297      not_inetd = 1;
1298      break;
1299    case 'f':
1300      spawn_on_accept = 0;
1301      not_inetd = 1;
1302      break;
1303#ifdef WIN32
1304    case 'I':
1305      child = TRUE;
1306      break;
1307    case 'i':
1308      break;
1309#endif
1310    case 'L':
1311      not_inetd = 1;
1312      break_args_explicit(optarg,arg1,arg2);
1313      if (arg1[0]) {
1314	strncpy(local_host_name,arg1,sizeof(local_host_name));
1315      }
1316      if (arg2[0]) {
1317	local_address_family = parse_address_family(arg2);
1318      }
1319      break;
1320    case 'n':
1321      shell_num_cpus = atoi(optarg);
1322      if (shell_num_cpus > MAXCPUS) {
1323	fprintf(stderr,
1324		"netserver: This version can only support %d CPUs. Please"
1325		"increase MAXCPUS in netlib.h and recompile.\n",
1326		MAXCPUS);
1327	fflush(stderr);
1328	exit(1);
1329      }
1330      break;
1331    case 'N':
1332      suppress_debug = 1;
1333      debug = 0;
1334      break;
1335    case 'p':
1336      /* we want to open a listen socket at a specified port number */
1337      strncpy(listen_port,optarg,sizeof(listen_port));
1338      not_inetd = 1;
1339      break;
1340    case 'Z':
1341      /* only copy as much of the passphrase as could fit in the
1342	 test-specific portion of a control message. Windows does not
1343	 seem to have a strndup() so just malloc and strncpy it.  we
1344	 weren't checking the strndup() return so won't bother with
1345	 checking malloc(). we will though make certain we only
1346	 allocated it once in the event that someone puts -Z on the
1347	 command line more than once */
1348      if (passphrase == NULL)
1349	passphrase = malloc(sizeof(netperf_request.content.test_specific_data));
1350      strncpy(passphrase,
1351	      optarg,
1352	      sizeof(netperf_request.content.test_specific_data));
1353      passphrase[sizeof(netperf_request.content.test_specific_data) - 1] = '\0';
1354      break;
1355    case '4':
1356      local_address_family = AF_INET;
1357      break;
1358    case '6':
1359#if defined(AF_INET6)
1360      local_address_family = AF_INET6;
1361#else
1362      local_address_family = AF_UNSPEC;
1363#endif
1364      break;
1365    case 'v':
1366      /* say how much to say */
1367      verbosity = atoi(optarg);
1368      break;
1369    case 'V':
1370      printf("Netperf version %s\n",NETPERF_VERSION);
1371      exit(0);
1372      break;
1373
1374    }
1375  }
1376}
1377
1378void
1379daemonize() {
1380#if defined(HAVE_FORK)
1381
1382  if (debug) {
1383    fprintf(where,
1384	    "%s: enter\n",
1385	    __FUNCTION__);
1386    fflush(where);
1387  }
1388
1389  /* flush the usual suspects */
1390  fflush(stdin);
1391  fflush(stdout);
1392  fflush(stderr);
1393
1394  switch (fork()) {
1395  case -1:
1396    fprintf(stderr,
1397	    "%s: fork() error %s (errno %d)\n",
1398	    __FUNCTION__,
1399	    strerror(errno),
1400	    errno);
1401    fflush(stderr);
1402    exit(1);
1403  case 0:
1404
1405    /* perhaps belt and suspenders, but if we dump core, perhaps
1406       better to do so here. we won't worry about the call being
1407       successful though. raj 2011-07-08 */
1408    chdir(DEBUG_LOG_FILE_DIR);
1409
1410    /* we are the child. we should get a new "where" to match our new
1411       pid */
1412
1413    open_debug_file();
1414
1415#ifdef HAVE_SETSID
1416      setsid();
1417#else
1418      setpgrp();
1419#endif /* HAVE_SETSID */
1420
1421      signal(SIGCLD, SIG_IGN);
1422
1423      /* ok, we can start accepting control connections now */
1424      accept_connections();
1425
1426  default:
1427    /* we are the parent, nothing to do but exit? */
1428    exit(0);
1429  }
1430
1431#else
1432  fprintf(where,
1433	  "%s called on platform which cannot daemonize\n",
1434	  __FUNCTION__);
1435  fflush(where);
1436  exit(1);
1437#endif /* HAVE_FORK */
1438}
1439
1440static void
1441check_if_inetd() {
1442
1443  if (debug) {
1444    fprintf(where,
1445	    "%s: enter\n",
1446	    __FUNCTION__);
1447    fflush(where);
1448  }
1449
1450  if (not_inetd) {
1451    return;
1452  }
1453  else {
1454#if !defined(WIN32) && !defined(__VMS)
1455    struct sockaddr_storage name;
1456    netperf_socklen_t namelen;
1457
1458    namelen = sizeof(name);
1459    if (getsockname(0,
1460		    (struct sockaddr *)&name,
1461		    &namelen) == SOCKET_ERROR) {
1462      not_inetd = 1;
1463    }
1464    else {
1465      not_inetd = 0;
1466      child = 1;
1467    }
1468#endif
1469  }
1470}
1471
1472/* OK, so how does all this work you ask?  Well, we are in a maze of
1473   twisty options, all different.  Netserver can be invoked as a child
1474   of inetd or the VMS auxiliary server process, or a parent netserver
1475   process. In those cases, we could/should follow the "child"
1476   path. However, there are really two "child" paths through the
1477   netserver code.
1478
1479   When this netserver is a child of a parent netserver in the
1480   case of *nix, the child process will be created by a
1481   spawn_child_process() in accept_connections() and will not hit the
1482   "child" path here in main().
1483
1484   When this netserver is a child of a parent netserver in the case of
1485   windows, the child process will have been spawned via a
1486   Create_Process() call in spawn_child_process() in
1487   accept_connections, but will flow through here again. We rely on
1488   the scan_netserver_args() call to have noticed the magic option
1489   that tells us we are a child process.
1490
1491   When this netserver is launched from the command line we will first
1492   set-up the listen endpoint(s) for the controll connection.  At that
1493   point we decide if we want to and can become our own daemon, or
1494   stay attached to the "terminal."  When this happens under *nix, we
1495   will again take a fork() path via daemonize() and will not come
1496   back through main().  If we ever learn how to become our own daemon
1497   under Windows, we will undoubtedly take a Create_Process() path
1498   again and will come through main() once again - that is what the
1499   "daemon" case here is all about.
1500
1501   It is hoped that this is all much clearer than the old spaghetti
1502   code that netserver had become.  raj 2011-07-11 */
1503
1504
1505int _cdecl
1506main(int argc, char *argv[]) {
1507
1508#ifdef WIN32
1509  WSADATA	wsa_data ;
1510
1511  /* Initialize the winsock lib do we still want version 2.2? */
1512  if ( WSAStartup(MAKEWORD(2,2), &wsa_data) == SOCKET_ERROR ){
1513    printf("WSAStartup() failed : %lu\n", GetLastError()) ;
1514    return -1 ;
1515  }
1516#endif /* WIN32 */
1517
1518  /* Save away the program name */
1519  program = (char *)malloc(strlen(argv[0]) + 1);
1520  if (program == NULL) {
1521    printf("malloc for program name failed!\n");
1522    return -1 ;
1523  }
1524  strcpy(program, argv[0]);
1525
1526  init_netserver_globals();
1527
1528  netlib_init();
1529
1530  strncpy(local_host_name,"",sizeof(local_host_name));
1531  local_address_family = AF_UNSPEC;
1532  strncpy(listen_port,TEST_PORT,sizeof(listen_port));
1533
1534  scan_netserver_args(argc, argv);
1535
1536  check_if_inetd();
1537
1538  if (child) {
1539    /* we are the child of either an inetd or parent netserver via
1540       spawning (Windows) rather than fork()ing. if we were fork()ed
1541       we would not be coming through this way. set_server_sock() must
1542       be called before open_debug_file() or there is a chance that
1543       we'll toast the descriptor when we do not wish it. */
1544    set_server_sock();
1545    open_debug_file();
1546    process_requests();
1547  }
1548  else if (daemon_parent) {
1549    /* we are the parent daemonized netserver
1550       process. accept_connections() will decide if we want to spawn a
1551       child process */
1552    accept_connections();
1553  }
1554  else {
1555    /* we are the top netserver process, so we have to create the
1556       listen endpoint(s) and decide if we want to daemonize */
1557    setup_listens(local_host_name,listen_port,local_address_family);
1558    if (want_daemonize) {
1559      daemonize();
1560    }
1561    accept_connections();
1562  }
1563
1564  unlink_empty_debug_file();
1565
1566#ifdef WIN32
1567  WSACleanup();
1568#endif
1569
1570  return 0;
1571
1572}
1573