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