1/* -*- c-basic-offset: 8; -*-
2 *
3 * Copyright (c) 1993 W. Richard Stevens.  All rights reserved.
4 * Permission to use or modify this software and its documentation only for
5 * educational purposes and without fee is hereby granted, provided that
6 * the above copyright notice appear in all copies.  The author makes no
7 * representations about the suitability of this software for any purpose.
8 * It is provided "as is" without express or implied warranty.
9 */
10
11
12#include <stdio.h>
13#include <netdb.h>
14#include <sys/types.h>
15#include <netinet/in.h>
16#include <arpa/inet.h>
17#include "sock.h"
18
19int
20servopen(char *host, char *port)
21{
22	int					fd, newfd, i, on, pid;
23	char				*protocol;
24	struct in_addr		inaddr;
25	struct servent		*sp;
26
27	protocol = udp ? "udp" : "tcp";
28
29		/* Initialize the socket address structure */
30	bzero(&servaddr, sizeof(servaddr));
31	servaddr.sin_family      = AF_INET;
32
33		/* Caller normally wildcards the local Internet address, meaning
34		   a connection will be accepted on any connected interface.
35		   We only allow an IP address for the "host", not a name. */
36	if (host == NULL)
37		servaddr.sin_addr.s_addr = htonl(INADDR_ANY);		/* wildcard */
38	else {
39		if (inet_aton(host, &inaddr) == 0)
40			err_quit("invalid host name for server: %s", host);
41		servaddr.sin_addr = inaddr;
42	}
43
44		/* See if "port" is a service name or number */
45	if ( (i = atoi(port)) == 0) {
46		if ( (sp = getservbyname(port, protocol)) == NULL)
47			err_ret("getservbyname() error for: %s/%s", port, protocol);
48
49		servaddr.sin_port = sp->s_port;
50	} else
51		servaddr.sin_port = htons(i);
52
53	if ( (fd = socket(AF_INET, udp ? SOCK_DGRAM : SOCK_STREAM, 0)) < 0)
54		err_sys("socket() error");
55
56	if (reuseaddr) {
57		on = 1;
58		if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
59			err_sys("setsockopt of SO_REUSEADDR error");
60	}
61
62#ifdef	SO_REUSEPORT
63	if (reuseport) {
64		on = 1;
65		if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0)
66			err_sys("setsockopt of SO_REUSEPORT error");
67	}
68#endif
69
70		/* Bind our well-known port so the client can connect to us. */
71	if (bind(fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
72		err_sys("can't bind local address");
73
74	join_mcast(fd, &servaddr);
75
76	if (udp) {
77		buffers(fd);
78
79		if (foreignip[0] != 0) {	/* connect to foreignip/port# */
80			bzero(&cliaddr, sizeof(cliaddr));
81			if (inet_aton(foreignip, &cliaddr.sin_addr) == 0)
82				err_quit("invalid IP address: %s", foreignip);
83			cliaddr.sin_family = AF_INET;
84			cliaddr.sin_port   = htons(foreignport);
85				/* connect() for datagram socket doesn't appear to allow
86				   wildcarding of either IP address or port number */
87
88			if (connect(fd, (struct sockaddr *) &cliaddr, sizeof(cliaddr))
89																		  < 0)
90				err_sys("connect() error");
91
92		}
93
94		sockopts(fd, 1);
95
96		return(fd);		/* nothing else to do */
97	}
98
99	buffers(fd);		/* may set receive buffer size; must do here to get
100						   correct window advertised on SYN */
101	sockopts(fd, 0);	/* only set some socket options for fd */
102
103	listen(fd, listenq);
104
105	if (pauselisten)
106		sleep_us(pauselisten*1000);		/* lets connection queue build up */
107
108	if (dofork)
109		TELL_WAIT();			/* initialize synchronization primitives */
110
111	for ( ; ; ) {
112		i = sizeof(cliaddr);
113		if ( (newfd = accept(fd, (struct sockaddr *) &cliaddr, &i)) < 0)
114			err_sys("accept() error");
115
116		if (dofork) {
117			if ( (pid = fork()) < 0)
118				err_sys("fork error");
119
120			if (pid > 0) {
121				close(newfd);	/* parent closes connected socket */
122				WAIT_CHILD();	/* wait for child to output to terminal */
123				continue;		/* and back to for(;;) for another accept() */
124			} else {
125				close(fd);		/* child closes listening socket */
126			}
127		}
128
129			/* child (or iterative server) continues here */
130		if (verbose) {
131				/* Call getsockname() to find local address bound to socket:
132				   local internet address is now determined (if multihomed). */
133			i = sizeof(servaddr);
134			if (getsockname(newfd, (struct sockaddr *) &servaddr, &i) < 0)
135				err_sys("getsockname() error");
136
137						/* Can't do one fprintf() since inet_ntoa() stores
138						   the result in a static location. */
139			fprintf(stderr, "connection on %s.%d ",
140					INET_NTOA(servaddr.sin_addr), ntohs(servaddr.sin_port));
141			fprintf(stderr, "from %s.%d\n",
142					INET_NTOA(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
143		}
144
145		buffers(newfd);		/* setsockopt() again, in case it didn't propagate
146							   from listening socket to connected socket */
147		sockopts(newfd, 1);	/* can set all socket options for this socket */
148
149		if (dofork)
150			TELL_PARENT(getppid());	/* tell parent we're done with terminal */
151
152		return(newfd);
153	}
154}
155